继承与友元
友元关系不能继承,基类友元不能访问派生类私有和保护成员
class Person { public: friend void Display(const Person& p,const Student s); Person(const char* name = "zhangsan") :_name(name) { cout << "Person()" << endl; } protected: string _name; }; class Student :public Person { public: Student(const char* name) :Person(name) , _stuid(210202) { cout << "Student()" << endl; } protected: int _stuid; }; void Display(const Person& p,const Student s) { cout << p._name << endl; cout << s._stuid << endl; } int main() { Person p("zhangsan"); Student s("lisi"); Display(p,s); return 0; }
继承与静态成员
如果基类定义了static静态成员,则整个继承体系里面只有一个这样的成员
class Person { public: Person() { ++_count; } protected: string _name; public: static int _count;//统计人数 }; int Person::_count = 0; class Student :public Person { protected: int _stuid; }; int main() { Person p; Student s; p._count++; cout << p._count << endl; cout << &p._count << endl; s._count++; cout << s._count << endl; cout << &s._count << endl; return 0; }
打印的地址都一样,说明static成员,并不在类中,而是存储在静态区中的;静态成员属于整个类,所有对象,同时也属于所有派生类及对象
复杂的菱形继承及菱形虚拟继承
单继承:一个子类只有一个直接父类的继承关系
多继承:一个子类有两个或者以上直接父类的继承关系
菱形继承:多继承的一种
菱形继承存在着某些问题,观察下列代码
class Person { public: string _name; }; class Student :public Person { protected: int _stuid; }; class Teacher :public Person { protected: int _jodid; }; class Assistant :public Teacher, public Student { protected: string _majorcourse; }; int main() { Assistant a; a._name = "zhangsan"; return 0; }
程序运行之后便会报错,因为Assistant a中有两份_name,一份是Student,一份是Teacher的,所以使用时就造成了二义性;解决方式:使用时加上访问限定符
int main() { Assistant a; a.Student::_name = "zhangsan"; a.Teacher::_name = "lisi"; return 0; }
指定作用域并没有彻底地解决菱形继承所造成的问题,接下来介绍虚拟继承来彻底地解决问题
在 Student和 Teacher中继承 Person时使用虚拟继承
具体操作如下
class Person { public: string _name; }; class Student :virtual public Person { protected: int _stuid; }; class Teacher :virtual public Person { protected: int _jodid; }; class Assistant :public Teacher, public Student { protected: string _majorcourse; }; int main() { Assistant a; a._name = "zhangsan"; return 0; }
既然已经知道虚拟继承可以解决菱形继承存在的问题,那么原理是什么呢?要做到知其然知其所以然,接下来就深入学习虚拟继承的原理
虚拟继承的原理
观察下列代码
菱形继承:
class A { public: int _a; }; class B :public A { public: int _b; }; class C :public A { public: int _c; }; class D :public B, public C { public: int _d; }; int main() { D d; d.B::_a = 1; d.C::_a = 2; d._b = 3; d._c = 4; d._d = 5; return 0; }
通过查看内存,可以很清楚地发现数据冗余
菱形虚拟继承:
class A { public: int _a; }; class B :virtual public A { public: int _b; }; class C :virtual public A { public: int _c; }; class D :public B, public C { public: int _d; }; int main() { D d; d.B::_a = 1; d.C::_a = 2; d._b = 3; d._c = 4; d._d = 5; return 0; }
通过查看内存,B和C中都存在一个指针,分别对指针进行取地址发现,指针所指向的是虚基表,其中保存着距离虚基类对象_a的偏移量(第二行),通过偏移量可以计算出B和C中虚拟继承的_a的位置,在上面已经标注出来。
虚拟继承原理解释如下
继承和组合
- public继承是一种is a的关系,每个派生类对象中都是一个基类对象
- 组合是一种has a的关系,比如B组合了A,则每个B对象中都有一个A对象