什么是private继承
我们知道,class的默认访问权限是private的,这一点同样适用于继承,如果忘记加访问权限修饰,class的默认继承方式就是private。但private继承并非要表达继承关系,而是表达 ”根据某物实现出“的概念,这一概念跟我们通常所说的组合方式(has-a)类似,也就是说,private继承关系通常也是表达了组合关系,而非继承。
private 继承的特点
继承的所有成员的访问权限都变为private。这一点的保证主要来自于private的继承权限修饰,由此,无论base class的成员由何种方式修饰,最终都将得到private限定。即使发生重写(virtual函数的重写),任何重新设定的访问限定都是无效的(例如,限定原private virtual 函数重写后的访问权限为public,事实上不会有任何作用,反而误导类的使用者)。
private 不表示 “is a”的关系,因此不会发生隐式类型转换。具体有如下的例子:
class A {
};
class B:private A {
};
void func(A& a){}
int main() {
A a;
B b;
func(a); // OK
func(b); // 错误,不是is a,因此 B不是A,调用失败
}
private继承的上述特性还是需要注意的。
在何时使用private继承
首先,必须明确的一点是,private继承的优先级低于组合,尽量使用组合,但private继承也有一些表现优秀的使用场景:
- 需要重写base class的virtual函数或使用其protect成员:
这种情况下,事实上也可以用组合代替,在private限定下,重新定义一个class继承自base class,也可实现这种需求,但这里不做讨论,只需要知道实现这种需求的方法不止一种即可。在private 继承的情况下,我们就可以重写private版本的virtual函数,从而实现对base class 中该功能的重新定义(具体例子请看条款40)。这里要注意的一点是:C++允许对private的虚函数重写(条款35提到这是模板方法设计模式的一种特例),因此,在本书中提到,这也许是private继承的缺陷,按照当时C++ 03的版本,确实无法阻止子类对该private 虚函数的重写,但是 C++ 11 的final关键字可以阻止重写,这一缺陷就不复存在了,具体来讲,一个这样的写法可以避免private virtual 被重写:
class A {
public:
virtual void vfunc();
};
class B: private A {
private:
virtual void vfunc() final; // C++ 11
};
2 . EBO(空白基类最优化) (重要)
编译器的EBO也是一个重要的点,这个点可以帮我们引申出一个小的考点,那就是一个独立的empty的class的size到底是多少,我们知道,这个大小通常为1,因为C++规定独立对象都必须有非零大小,因此编译器会安插一个char到该类中,所以size的结果为1.我们必须明确的一点,我们认为只要不包含non-static成员的类都可视作empty class,包括那些包含typedef以及static成员的类。
EBO是指,在发生单一继承关系时,empty class所占的空间1会被忽略。对于多继承,并不适用,此外,并不限定在private继承。
class A{};
class B:private A{
int x;
};
sizeof(A) = 1, sizeof(B) = 4; // 继承发生EBO
EBO被广泛使用在STL库中。
总结
- private继承相对组合具有较低使用优先级。
- private继承不具备is a的关系,因此隐式类型转换不存在。
- 在需要重写virtual函数时使用private继承。
- 在EBO中使用private继承。