问题的引出:
一个类D继承自两个类B1,B2,而B1,B2都继承与基类B
#include<iostream> using namespace std; class B { public: int ib; char cb; public: B():ib(0),cb('B') { printf("B found\n"); } virtual void f() { cout << "B::f()" << endl;} virtual void Bf() { cout << "B::Bf()" << endl;} }; class B1 : public B { public: int ib1; char cb1; public: B1():ib1(11),cb1('1') { printf("B1 found\n"); } virtual void f() { cout << "B1::f()" << endl;} virtual void f1() { cout << "B1::f1()" << endl;} virtual void Bf1() { cout << "B1::Bf1()" << endl;} }; class B2: public B { public: int ib2; char cb2; public: B2():ib2(12),cb2('2') { printf("B2 found\n"); } virtual void f() { cout << "B2::f()" << endl;} virtual void f2() { cout << "B2::f2()" << endl;} virtual void Bf2() { cout << "B2::Bf2()" << endl;} }; class D : public B1, public B2 { public: int id; char cd; public: D():id(100),cd('D') { printf("D found\n"); } virtual void f() { cout << "D::f()" << endl;} virtual void f1() { cout << "D::f1()" << endl;} virtual void f2() { cout << "D::f2()" << endl;} virtual void Df() { cout << "D::Df()" << endl;} }; int main(void) { D* d = new D; //d->ib=111;由于二义性 这样会有问题 //printf("%d\n",d->ib); d->B1::ib=222; //printf("%d\n",d->ib); d->B2::ib=333; //printf("%d\n",d->ib); }
明显这会带来二义性的问题,同时也会明显浪费内存空间。
以前在上课的了解过虚基类,虚继承(一个子类通过虚拟继承了一个基类,这种情况这个基类可以叫做虚基类),却不知道这到底有什么用,在这里就派上用场了
◇概念:
C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。
语法:
class X:virtual public base
这样就可以解决二义性的问题了
#include<iostream> using namespace std; class B { public: int ib; char cb; public: B():ib(0),cb('B') { printf("B found\n"); } virtual void f() { cout << "B::f()" << endl;} virtual void Bf() { cout << "B::Bf()" << endl;} }; class B1 :virtual public B { public: int ib1; char cb1; public: B1():ib1(11),cb1('1') { printf("B1 found\n"); } virtual void f() { cout << "B1::f()" << endl;} virtual void f1() { cout << "B1::f1()" << endl;} virtual void Bf1() { cout << "B1::Bf1()" << endl;} }; class B2: virtual public B { public: int ib2; char cb2; public: B2():ib2(12),cb2('2') { printf("B2 found\n"); } virtual void f() { cout << "B2::f()" << endl;} virtual void f2() { cout << "B2::f2()" << endl;} virtual void Bf2() { cout << "B2::Bf2()" << endl;} }; class D : public B1, public B2 { public: int id; char cd; public: D():id(100),cd('D') { printf("D found\n"); } virtual void f() { cout << "D::f()" << endl;} virtual void f1() { cout << "D::f1()" << endl;} virtual void f2() { cout << "D::f2()" << endl;} virtual void Df() { cout << "D::Df()" << endl;} }; int main(void) { D* d = new D; d->ib=111;//由于二义性 这样会有问题 printf("%d\n",d->ib); d->B1::ib=222; d->B2::ib=333; printf("%d\n",d->B1::ib); printf("%d\n",d->B2::ib); printf("%d\n",d->ib); }
虚函数之前已经讲过,这里再说说纯虚函数,与其说纯虚函数,还不如说是抽象类,它只是声明一个函数但不实现它,让派生类去实现它
class Vehicle { public: virtual void PrintTyre()=0; //纯虚函数是这样定义的 }; class Camion:public Vehicle { public: virtual void PrintTyre(){cout<<"Camion tyre four"<<endl;}; }; class Bike:public Vehicle { public: virtual void PrintTyre(){cout<<"Bike tyre two"<<endl;}; }; void main() { Camion c; Bike b; b.PrintTyre(); c.PrintTyre(); }
总结
虚基类
1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。
4, 最派生类是指在继承结构中建立对象时所指定的类。
5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。
7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。
虚函数
1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。
2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。
3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。
4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。
5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。 纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。
纯虚函数
1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。
2, 纯虚函数的作用是为派生类提供一个一致的接口。
3, 纯虚函数不能实化化,但可以声明指针。
参考:http://www.cnblogs.com/ms-frank/archive/2008/01/16/1041310.html