友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
//验证友元不能继承
class B
{friend void Print();
public:B(int b): _b(b){cout << "B()" << endl;}protected:int _b;
};class D : public B
{
public:D(int b, int d): B(b), _d(_d){cout << "D(int,int)" << endl;}protected:int _d;
};void Print()
{B b(1);cout << b._b << endl;//结论:友元关系不能被继承//创建一个子类对象,如果子类对象D中的_d不能被访问,说明友元不能被继承D d(1, 2);//cout << d._d << endl; 编译失败
}
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例
// 静态成员可以被子类继承,并且在整个继承体系中只存在一份
class B
{
public:static int _b;
};int B::_b = 10;class D : public B
{
protected:int _d;
};class DD : public B
{
protected:int _d;
};int main()
{cout << D::_b << endl;cout << DD::_b << endl;cout << &B::_b << " " << &D::_b <<" "<<&DD::_b << endl;return 0;
}
地址一样,成功继承。说明静态成员可以被子类继承,并且在整个继承体系中只存在一份
对象模型:对象中的成员在内存中的存储方式
一个子类只有一个直接父类时称这个继承关系为单继承
一个子类有两个或以上直接父类时称这个继承关系为多继承
注意:如果有多个基类,每个基类前必须要添加继承权限,否则就是默认的继承权限
菱形继承是多继承的一种特殊情况
class B
{
public:int _b;
};class C1 : public B
{
public:int _c1;
};class C2 : public B
{
public:int _c2;
};class D : public C1,public C2
{
public:int _d;
};
int main()
{cout << sizeof(D) << endl;D d;return 0;
}
对上面代码进行如下操作:
菱形继承继承的二义性问题:
d._b = 1; // 编译失败:error C2385: 对“_b”的访问不明确
d.func(); // 编译失败:error C2385: 对“_b”的访问不明确
错误分析:
* D从C1中继承下来一个_b,从C2中也继承下来一个_b,导致D的模型中实际有2个_b
* 如果通过D的对象直接访问_b,编译器就不知道到底要访问从C1中继承下来的_b还是要访问
* 从C2中继承下来的_b
* 因此存在二义性
* 即:菱形继承的缺陷 因此:一般情况下尽量避免设计出菱形继承方式
菱形继承二义性问题解决方式:
1. 让访问明确化: 在成员前加类名以及作用域限定符
d.C1::_b = 1;
d._c1 = 2;
d.C2::_b = 3;
d._c2 = 4;
d._d = 5;
上述解决方式只是让代码通过编译了,但是最顶层基类中的成员在子类中仍旧是存在多份 /
不足:最顶层基类中的成员在子类中仍旧是存在多份,浪费空间,而且二义性并没有真正的解决
如果要真正解决,让最顶层类中的成员在子类(类型继承中最底下的类)只存储一份,在C++中引入了菱形虚拟继承解决上述问题
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。需要注意的是,虚拟继承不要在其他地方去使用
注意:平时在写继承的代码时,不会涉及虚拟继承的方式
虚拟继承只有一个作用:解决菱形继承二义性的问题
在继承权限前加上虚拟关键字即可
class B
{
public:int _b;
};class D : virtual public B
{
public:int _d;
};int main()
{cout << sizeof(D) << endl; // 12, 普通继承是8D d;d._b = 1;d._d = 2; // 直接一条mov指令搞定return 0;
}
利用菱形虚拟继承解决菱形继承中二义性问题
class A
{public:int _a;
};
// class B : public A
class B : virtual public A
{public:int _b;
};
// class C : public A
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;
}
D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A
1. C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱
形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设
计出菱形继承。否则在复杂度及性能上都有问题。
2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。
3. 继承和组合
public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象