class Base1
{
public:
Base1(){abc = 1;}
~Base1(){}
void testFunc(){printf("This is Base1's Func~!/n");}
int abc;
};
class Base2
{
public:
Base2(){abc = 2;}
~Base2(){}
void testFunc(){printf("This is Base2's Func~!/n");}
int abc;
};
class son: public Base1, public Base2
{
public:
son(){}
~son(){}
void Showabc(){printf("abc:%d/n", Base1::abc);}
};
int main()
{
son testit;
testit.Showabc();
testit.Base2::testFunc();
return 1;
}
可以看到,由于两个基类的成员函数和成员变量有重名,我们在引用的时候必须指明是哪一个基类的成员,否则就会无法编译通过出现类似如下的提示:
Compiling...
main.cpp
f:/new study material/vc6test/main.cpp(76) : error C2385: 'son::testFunc' is ambiguous
f:/new study material/vc6test/main.cpp(76) : warning C4385: could be the 'testFunc' in base 'Base1' of class 'son'
f:/new study material/vc6test/main.cpp(76) : warning C4385: or the 'testFunc' in base 'Base2' of class 'son'
这说明编译器搞不清楚你要使用哪一个testFunc……
以下为转载:
C++允許讓衍生類別同時直接繼承兩個以上的父類別,例如有A、B兩個類別,您可以使用下面的方式來讓C類別直接繼承這兩個類別:
public:
C(int x) : A(x), B(x) { // 多重繼承下的建構函式呼叫
// 實作
}
// 實作
};
上面也同時示範了在繼承之後的的建構函式呼叫,沒有參數的預設建構函式不用使用這種方式執行,它會自動被呼叫,而解構函式也是會自動執行。
多重繼承之後的建構函式執行順序,是依您撰寫程式時的順序由左而右決定,最後才是繼承後衍生類別,例如上面的例子中,會先執行A類別定義的建構函式,然後再執行B類別的建構函式,最後執行C類別的建構函式,而解構函式的執行則正好相反,先執行衍生類別的解構函式,然後再右向左執行,使用下面這個簡單的範例來示範:
#include <iostream>
using namespace std;
class FooA {
public:
FooA() {
cout << "執行FooA建構函式" << endl;
}
~FooA() {
cout << "執行FooA解構函式" << endl;
}
};
class FooB {
public:
FooB() {
cout << "執行FooB建構函式" << endl;
}
~FooB() {
cout << "執行FooB解構函式" << endl;
}
};
class FooC : public FooA, public FooB {
public:
FooC() {
cout << "執行FooC建構函式" << endl;
}
~FooC() {
cout << "執行FooC解構函式" << endl;
}
};
int main() {
FooC c;
cout << endl;
return 0;
}
執行結果:
執行FooA建構函式 執行FooB建構函式 執行FooC建構函式 執行FooC解構函式 |
多重繼承時通常其中一個基底類別作為private實作體,而其它的用以表現完全的抽象介面。
考慮您有一個方法doRequest(),您事先並無法知道什麼型態的物件會被傳進來,或者是這個方法可以接受任何類型的物件,您想要操作物件上的某個特定方法,例如doSomething()方法,問題是傳進來的物件是任意的,除非您定義一個抽象類別並宣告doSomething()抽象方法,然後讓所有的類別都繼承這個抽象類別,否則的話您的doRequest()方法似乎無法實作出來,實際上這麼作也沒有價值。
您可以藉由多重繼承來解決這個問題,例如先定義一個抽象類別:
- IRequest.h
#ifndef IREQUEST
#define IREQUEST
class IRequest {
public:
virtual void execute() = 0;
};
#endif
假設您的類別是以下的SomeObject類別的子類別:
- SomeObject.h
#ifndef SOMEOBJECT
#define SOMEOBJECT
#include <iostream>
using namespace std;
class SomeObject {
public:
virtual void someFunction() {
cout << "do something" << endl;
}
private:
void otherFunction() {
cout << "do other" << endl;
}
};
#endif
為了滿足之前所提供的doRequest()的需求,您讓衍生類別同時繼承IRequest與SomeObject類別,例如:
- Welcome.h
#include "IRequest.h"
#include "SomeObject.h"
#include <iostream>
using namespace std;
class Welcome : public SomeObject, public IRequest {
public:
void execute() {
cout << "Welcome!!" << endl;
}
};
- Hello.h
#include "IRequest.h"
#include "SomeObject.h"
#include <iostream>
using namespace std;
class Hello : public SomeObject, public IRequest {
public:
void execute() {
cout << "Hello!!" << endl;
}
};
假設您設計了一個doRequest()方法,雖然不知道Hello與 Welcome是什麼型態,但它們都繼承了IRequest,所以doRequest()只要知道IRequest定義了什麼公開介面,就可以操作Hello與Welcome的實例,而不用知道傳入的物件到底是什麼類別的實例,使用下面這個程式來測試:
- main.cpp
#include <iostream>
#include "IRequest.h"
#include "Welcome.h"
#include "Hello.h"
using namespace std;
void doRequest(IRequest *request) {
request->execute();
}
int main() {
Welcome welcome;
Hello hello;
doRequest(&welcome);
doRequest(&hello);
return 0;
}
執行結果:
Welcome!! Hello!! |
在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个问题,C++引入了多重继承的概念,C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承。
举个例子,交通工具类可以派生出汽车和船连个子类,但拥有汽车和船共同特性水陆两用汽车就必须继承来自汽车类与船类的共同属性。
由此我们不难想出如下的图例与代码:
当一个派生类要使用多重继承的时候,必须在派生类名和冒号之后列出所有基类的类名,并用逗好分隔。
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(int weight = 0)
{
Vehicle::weight = weight;
}
void SetWeight(int weight)
{
cout<<"重新设置重量"<<endl;
Vehicle::weight = weight;
}
virtual void ShowMe() = 0;
protected:
int weight;
};
class Car:public Vehicle//汽车
{
public:
Car(int weight=0,int aird=0):Vehicle(weight)
{
Car::aird = aird;
}
void ShowMe()
{
cout<<"我是汽车!"<<endl;
}
protected:
int aird;
};
class Boat:public Vehicle//船
{
public:
Boat(int weight=0,float tonnage=0):Vehicle(weight)
{
Boat::tonnage = tonnage;
}
void ShowMe()
{
cout<<"我是船!"<<endl;
}
protected:
float tonnage;
};
class AmphibianCar:public Car,public Boat//水陆两用汽车,多重继承的体现
{
public:
AmphibianCar(int weight,int aird,float tonnage)
:Vehicle(weight),Car(weight,aird),Boat(weight,tonnage)
//多重继承要注意调用基类构造函数
{
}
void ShowMe()
{
cout<<"我是水陆两用汽车!"<<endl;
}
};
int main()
{
AmphibianCar a(4,200,1.35f);//错误
a.SetWeight(3);//错误
system("pause");
}
上面的代码从表面看,看不出有明显的语发错误,但是它是不能够通过编译的。这有是为什么呢?
这是由于多重继承带来的继承的模糊性带来的问题。
先看如下的图示:
在图中深红色标记出来的地方正是主要问题所在,水陆两用汽车类继承了来自Car类与Boat类的属性与方法,Car类与Boat类同为AmphibianCar类的基类,在内存分配上AmphibianCar获得了来自两个类的SetWeight()成员函数,当我们调用a.SetWeight(3)的时候计算机不知道如何选择分别属于两个基类的被重复拥有了的类成员函数SetWeight()。
由于这种模糊问题的存在同样也导致了AmphibianCar a(4,200,1.35f);执行失败,系统会产生Vehicle”不是基或成员的错误。
以上面的代码为例,我们要想让AmphibianCar类既获得一个Vehicle的拷贝,而且又同时共享用Car类与Boat类的数据成员与成员函数就必须通过C++所提供的虚拟继承技术来实现。
我们在Car类和Boat类继承Vehicle类出,在前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会自动先加入一个Vehicle的拷贝,当再次请求一个Vehicle的拷贝的时候就会被忽略,保证继承类成员函数的唯一性。
修改后的代码如下,注意观察变化:
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(int weight = 0)
{
Vehicle::weight = weight;
cout<<"载入Vehicle类构造函数"<<endl;
}
void SetWeight(int weight)
{
cout<<"重新设置重量"<<endl;
Vehicle::weight = weight;
}
virtual void ShowMe() = 0;
protected:
int weight;
};
class Car:virtual public Vehicle//汽车,这里是虚拟继承
{
public:
Car(int weight=0,int aird=0):Vehicle(weight)
{
Car::aird = aird;
cout<<"载入Car类构造函数"<<endl;
}
void ShowMe()
{
cout<<"我是汽车!"<<endl;
}
protected:
int aird;
};
class Boat:virtual public Vehicle//船,这里是虚拟继承
{
public:
Boat(int weight=0,float tonnage=0):Vehicle(weight)
{
Boat::tonnage = tonnage;
cout<<"载入Boat类构造函数"<<endl;
}
void ShowMe()
{
cout<<"我是船!"<<endl;
}
protected:
float tonnage;
};
class AmphibianCar:public Car,public Boat//水陆两用汽车,多重继承的体现
{
public:
AmphibianCar(int weight,int aird,float tonnage)
:Vehicle(weight),Car(weight,aird),Boat(weight,tonnage)
//多重继承要注意调用基类构造函数
{
cout<<"载入AmphibianCar类构造函数"<<endl;
}
void ShowMe()
{
cout<<"我是水陆两用汽车!"<<endl;
}
void ShowMembers()
{
cout<<"重量:"<<weight<<"顿,"<<"空气排量:"<<aird<<"CC,"<<"排水量:"<<tonnage<<"顿"<<endl;
}
};
int main()
{
AmphibianCar a(4,200,1.35f);
a.ShowMe();
a.ShowMembers();
a.SetWeight(3);
a.ShowMembers();
system("pause");
}
注意观察类构造函数的构造顺序。
虽然说虚拟继承与虚函数有一定相似的地方,但读者务必要记住,他们之间是绝对没有任何联系的!