Effective C++ 条款39 明智而审慎地使用private继承

1. public继承表明is-a的关系,要求接口的完全继承,而private继承表明"根据某物实现出的关系",要求仅仅继承实现,private继承有两个规则:

    1). 经由private继承而来的基类的所有成员在派生类中都会变成private属性

    2). 由于1),编译器不允许将派生类转为基类以防止对派生类private成员的非法访问.

2. 由条款38,private继承和复合具有相同作用——"根据某物实现出".两者之间,要尽可能使用复合,除非必要情况.必要情况指的是protected成员和virtual函数牵扯进的时候以及某个极端情况(稍后会说明)下空间限制严格的时候.

复合对于private继承的优势:

假设要实现一个Widget类,而它要用到一个Time类,也就是说,要"根据Time类实现Widget类",

如果用private继承,可能像这样:

class Widget:private Timer{
public:
    ...
private:
    ...
}
View Code

如果用复合,可能像这样:

class Widget{
public:
    ...
private:
    Time timer;
    ...
}
View Code

(将timer设为private,因为Widget类并不打算对客户端开放Time的接口)

有两个方面可以体现复合对于private继承的优势:

1). private继承不能阻止Widget的派生类对timer的某个virtual函数进行重定义,这可能是Widget的设计者不想看到的.

2). 继承提高了文件之间的编译依存性,因为Widget继承了Time,因而定义Widget的头文件需要include定义Time的头文件,而使用复合的话只需要将Widget内的Time替换为Time*即可

当然,也有两种情况使用private继承更好:

1). "当一个意欲成为derived class者想访问一个意欲成为base class者的protected部分,或为了重定义一或多个virtual函数,但这两个classes之间的概念关系其实是'is-implementations-in-terms-of'而非is-a"时.

2). private继承可以使编译器进行EBO(empty base optimization,空白基类最优化),所谓空白基类最优化,即对于以下empty类:

class Empty{
}

如果采用采用以下复合:

class HoldsAnInt{
    int a;
    Empty e;
}
View Code

那么一个HolderAsInt类将不止占据4字节(可能会占据8字节),因为对于"大小为零之独立(非附属)对象",C++标准要求安插一个char到其中以支持取址.(又由于字节对齐,HoldersAsInt可能会占据8字节)

而如果采用private继承,一个HoldersAsInt仅需4字节,Empty对象将不再占据空间,即:

class HoldersAsInt:private Empty{
    int a;
}
View Code

这就是编译器实行的EBO.当然,现实中使用的"empty classes"并非真的什么都不包含,虽然它们没有non-static对象,但往往有typedefs,enum,static成员变量,或non-virtual函数(STL就有许多有技术用途的empty classes,其中内含有用的成员(通常是typedefs),包括base classes unary_function和binary_function,这些是"用户自定义之函数对象"通常会继承的classes,由于EBO,可以避免derived classes大小的增加).

3. 总结:

    1). private继承和复合都意味着is-implementation-in-terms-of,但复合比较容易理解,优先选择复合.

    2). 面对并不存在is-a关系的两个类且其中一个需要访问另一个的protected成员或需要重新定义其virtual函数,private继承可能是最佳选择,此外,private复合所允许的EBO对致力于"对象尺寸最小化"的程序库开发者而言可能很重要.

原文地址:https://www.cnblogs.com/reasno/p/4799345.html