一、单继承&多继承&菱形继承
单继承与多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承
菱形继承引发的问题:
数据冗余
例:B、C中的_a都是来自于A,其实两个_a是同一个,但是因为B、C各自继承,产生了两份,造成了数据冗余
二义性
例:由于D继承于B、C,B、C继承于A,所以D中的_a不知道是来自B还是来自C,于是产生了二义性。
二、虚继承-------解决菱形继承的数据冗余与二义性
将B和C的继承变为虚继承:
class B:virtual public A class C:virtual public A
不过虚继承只能解决这种有公有继承祖先的多继承,菱形继承,比如下面这个例子加上virtual就无法解决
class A1 { public: int a; }; class A2 { public: int a; }; class B :virtual public A1, virtual public A2 { public: }; int main() { B b1; b1.a;//编译出错,访问不明确 system("pause"); return 0; }
这样我们只能自己解决,加上域限制符:
int main() { B b1; b1.A1::a=100; b1.A2::a=200; system("pause"); return 0; }
问题:
在菱形继承中加上virtual和不加virtual的父类大小有区别吗?
class A { public: int z; }; class base1:virtual public A//加上virtual { public: int a; }; class base2:public A//没有加virtual { public: int b; }; class stu:public base1,public base2 { public: int c; }; int main() { cout << sizeof(A) << endl; cout << sizeof(base1) << endl;//加上virtual cout << sizeof(base2) << endl;//没有加virtual system("pause"); return 0; } //结果为:4 12 8
由此分析:加上virtual后,编译器给变量增加了一个特殊的属性
三、虚函数
类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写:当在子类的定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
重写只发生在父类与子类之间。
class Person { public : virtual void BuyTickets() { cout<<" 买票"<< endl; } protected : string _name ; // 姓名 }; class Student : public Person { public : virtual void BuyTickets() { cout<<" 买票-半价 "<<endl ; } protected : int _num ; //学号 }; //void Fun(Person* p) void Fun (Person& p) { p.BuyTickets (); } void Test () { Person p ; Student s ; Fun(p ); Fun(s ); }
简单理解多态:
1.指向父类调父类,指向子类调子类。
2.构造函数不能为虚函数。
3.最好把父类的析构定义为虚函数,子类将重写父类的虚函数。
4.父类是虚函数,子类不是虚函数,但是子类继承了父类的属性,可以构成多态。
四、纯虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚 函数在派生类中重新定义以后,派生类才能实例化出对象。
class Person { virtual void Display () = 0; // 纯虚函数 protected : string _name ; // 姓名 }; class Student : public Person {};
重载、重写、重定义