effective-c++系列:private继承和多继承

private继承

  • 如果class之间的继承关系是private,那么编译器不会自动将一个子类对象转换为父类对象。
  • 使用private继承的所有父类成员,在子类中都是private类型的,即使成员在父类中是public或者protected的。
  • private意味着只有实现部分被继承,接口部分应略去。
  • private继承可以造成empty base最优化,如下代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Empty{};

    class A{
    private:
    int x;
    Empty e;
    };

    cout << sizeof(A);//8
    cout << sizeof(int);//4
    ```
    使用私有继承可以避免
    ```cpp
    class A : private Empty{
    int x;
    }
    cout << sizeof(A);//4
    cout << sizeof(int);//4

多重继承

  • 多重继承中的歧义
    看下面的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class A{
    public:
    void f1();
    };

    class B{
    public:
    void f1();
    };

    class Derived:
    public A,
    public B{

    };

    Derived d;
    d.f1(); //ambiguous! A::f1() or B::f1()

    在基类中定义命名相同的函数很容易造成这种问题,即使继承方式可能是私有或者方法名是私有,都会造成歧义。可以使用作用域+函数的方式显示声明要调用的函数,如A::f1();

  • 菱形继承

菱形继承
1
2
3
4
5
6
7
class File{
string filename;
};
class InputFile : public File{};
class OutputFile : public File{};
class IOFile: public InputFile,
public OutputFile{};
  • 这是一个典型的菱形继承,假设File类有个成员变量Filename,那么IOFile内有多少个Filename呢?有两种说法。
    IOFile从InputFile和OutputFile均复制了一份,存在两份filename。
    IOFile只应该有一份。
    c++默认的做法是第一种,产生两个filename成员。
  • 解决方式是virtual继承,也就是说File类必须是virtual base class
1
2
3
4
5
6
7
class File{
string filename;
};
class InputFile : virtual public File{};
class OutputFile : virtual public File{};
class IOFile: public InputFile,
public OutputFile{};

这样在IOFile内部就只有一份filename副本。
那么虚继承是怎么实现这个功能的呢。具体在虚继承内存布局章节会讨论。