我们已经知道public继承是is-a的关系,在public继承的情况下一个派生类可以暗自转换成一个基类,也就是在需要基类参数的地方传递派生类也是可以的,毕竟public继承是is-a的关系。但是对于private继承情况就不一样了,先看下面这段代码。
class Student :private Person
{
};
void eat(const Person& p)
{
}
void study(const Student &s)
{
}
int main()
{
Student s;
Person p;
//eat(s); //出错error C2243: “类型转换”: 从“Student *”到“const Person &”的转换存在,但无法访问
study(s);
return 0;
}
对于上面的student类,它private继承自Person,但是我们给一个需要基类Person的函数eat传递派生了student发现是行不通的,编译器报错,上面那个错误是我在用VS2013运行时报的错,这也就是说private继承与public继承是不同的,private继承不是is-a关系。
private继承其实意味着implemented-in-terms-of(根据某物实现出)。private继承意味着你要用基类已经具备的某些特性去实现派生类的一些功能。这个特点让我们想到了复合,复合也有这一功能,那么我们应该用哪一种呢?答案是尽量用复合,只在牵扯到protected成员和virtual函数时才用private。
如果我们设计一个类,要用到另一个类的某些特性,但我们设计的这个类又不能说是那个类,比如我们之前说过的用list实现set,我们不能说set是一个list,所以不能用public继承,在上一次的讲解中用的是复合,但是如果涉及到要使用虚函数和访问protected成员的操作,复合就不能实现了,而这一讲告诉我们可以用private继承。但是这不涉及protected成员和virtual函数时,还是尽量用复合。那么除此之外private继承还有一个好处,我们来看下面这段代码:
#include <iostream>
class Person{
};
class Student :private Person
{
private:
int ID;
};
class Worker
{
private:
int ID;
Person p;
};
int main()
{
Person p;
Student s;
Worker w;
std::cout << sizeof(p) << std::endl; //1
std::cout << sizeof(s) << std::endl; //4
std::cout << sizeof(w) << std::endl; //8
return 0;
}
首先,一个空类的大小不是0,是1,这主要是因为面对“大小为0的独立(非附属)对象”,通常C++官方勒令默默安插一个char到空对象内。所以sizeof(p)是1,而对于sizeof(w)由于存在内存对齐,编译器为其补充了3个空格,所以是8,但是如果是继承,因为不是独立对象了,C++就不进行默默安插char的操作了,所以对于sizeof(s),由于Person是空类,所以为4。这是所谓的EBO(empty base optimization:空白基类最优化)。EBO只在单一继承(而非多重继承)下才可行。
因为复合比较容易理解,所以无论什么时候,只要可以,选择复合而不是private继承。
请记住:
1.Private继承意味着is-implemented-in-terms-of(根据某物实现出),它通常比复合的级别低(意思就是优先考虑复合)。但是当派生类需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。
2.和复合不同,private继承可以是empty base最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能重要。