派生类

来自cppreference.com
< cpp‎ | language

任何类类型(以 class-key classstruct 声明者)可被声明为派生自一或多个基类,基类自身也可以派生自其基类,这组成继承层级。

基类的列表提供于类声明语法base-clausebase-clause 由字符 : 后随一或多个 base-specifier 的逗号分隔列表组成。

attr(可选) access-specifier(可选) virtual-specifier(可选) class-or-decltype
attr(C++11) - 可选的任何数量属性序列
access-specifier - privatepublicprotected 之一
virtual-specifier - 关键词 virtual

virtual-specifieraccess-specifier 可以任意顺序出现

base-clause 中的 base-specifier 可以是包展开

声明为 final 的 class 或 struct 不能出现于 base-clause

(C++11 起)

若省略 access-specifier ,则它对以类关键 struct 声明的类默认为 public ,对以类关键 class 声明的类为 private

struct Base {
    int a, b, c;
};
// 每个 Derived 类型对象包含 Base 为子对象
struct Derived : Base {
    int b;
};
// 每个 Derived2 类型对象包含 Derived 与 Base 为子对象
struct Derived2 : Derived {
    int c;
};

列于 base-clause 的类是直接基类。其基类是间接基类。同一类不能指定于直接基类多于一次,但同一类可以既是直接又是间接基类。

每个直接和间接基类都作为基类子对象,以实现定义的偏移存在于派生类的对象表示中。因为空基类优化,空基类通常不会增加派生类对象的大小。基类子对象的构造函数为派生类的构造函数所调用:可于成员初始化器列表向这些构造函数提供参数。

目录

[编辑] 虚基类

对于每个指定为 virtual 的相异基类,最终派生类对象仅含有该类型的一个基类子对象,即使该类在继承层级中出现多次(只要它每次都以 virtual 继承)。

struct B { int n; };
class X : public virtual B {};
class Y : virtual public B {};
class Z : public B {};
// 每个 AA 类型对象拥有一个 X ,一个 Y ,一个 Z 和二个 B :
// 其一是 Z 的基类,另一者为 X 与 Y 所共享
struct AA : X, Y, Z {
    void f() {
        X::n = 1; // 修改虚 B 基类子对象的成员
        Y::n = 2; // 修改同一虚 B 基类子对象的成员
        Z::n = 3; // 修改非虚 B 基类子对象的成员
 
        std::cout << X::n << Y::n << Z::n << '\n'; // 打印 223
    }
};

继承层级有虚基类的例子之一是标准库的 iostream 的继承层级: std::istreamstd::ostreamstd::ios 使用虚继承派生。 std::iostream 继承 std::istreamstd::ostream ,故每个 std::iostream 实例含一个 std::ostream 子对象、一个 std::istream 子对象和仅一个 std::ios 子对象(继而有一个 std::ios_base )。

所有虚基类子对象在任何非虚基类子对象前初始化,故只有最终派生类于其成员初始化器列表调用虚基类的构造函数:

struct B {
    int n;
    B(int x) : n(x) {}
};
struct X : virtual B { X() : B(1) {} };
struct Y : virtual B { Y() : B(2) {} };
struct AA : X, Y     { AA() : B(3), X(), Y() {} };
 
// AA 的默认构造函数调用 X 和 Y 的默认构造函数
// 但这些构造函数不调用 B 的构造函数,因为 B 是虚基类
AA a; // a.n == 3
// X 的默认构造函数调用 B 的构造函数
X x; // x.n == 1

涉及虚继承时,类成员的非限定名称查找有特殊规则(有时被引用为支配规则),见 unqualified_lookup#成员函数定义

[编辑] 公开继承

类使用 public 成员访问指定符从基类派生时,基类的所有公开成员可作为派生类的公开成员访问,基类的所有受保护成员可作为派生类的受保护成员访问(基类的私有成员决不可访问,除非设为友元)。

公开继承模拟面向对象编程的子类型关系:派生类对象是( IS-A )基类子对象。期待派生类对象的引用和指针,可为使用期待到其任何基类的引用和指针的代码所用(见 LSP ),或者为了 DbC ,派生类应该维护其公开基类的类不变量,不应强化任何其所覆写的成员函数的前置条件,或弱化任何其后置条件。

[编辑] 受保护继承

类使用 protected 成员访问指定符从基类派生时,基类的所有公开和受保护成员可作为派生类的受保护成员访问(基类的私有成员决不可访问,除非设为友元)。

受保护继承可用于“受控制的多态”:在派生类的成员中,还有在所有进一步派生类的成员中,派生类是( IS-A )基类:到派生类的引用和指针可用于期待到基类的引用和指针处。

[编辑] 私有继承

类使用 private 成员访问指定符从基类派生时,基类的所有公开和受保护成员可作为派生类的私有成员访问(基类的私有成员决不可访问,除非设为友元)。

私有继承常用于基于策略的设计,因为策略常是空基类,而使用基类可以启用静多态并活用空基类优化

私有继承亦可用于实现合成关系(基类子对象是派生类对象的实现细节)。成员使用提供更好的封装,而且通常受到偏好,除非派生类要求访问基类的受保护成员(包含构造函数)、需要覆写基类的虚成员、需要基类构造先于或析构后于某其他基类子对象,需要共享虚基类或需要控制虚基类的构造。实现合成的成员使用亦不可应用于从参数包多重继承的情况,或在编译时通过模板元编程确定基类身份的情况。

同受保护继承,私有继承亦可用于受控制的多态:在派生类的成员内(但不在进一步派生类内),派生类是( IS-A )基类。

template<typename Transport>
class service : Transport  // 从 Transport 策略私有继承
{
public:
    void transmit() {
        this->send(...);  // 发送传输所提供的任何内容
    }
};
// TCP 传输策略
class tcp {
public:
    void send(...);
};
// UDP 传输策略
class udp {
public:
    void send(...);
};
 
service<tcp> service(host, port); 
service.transmit(...); // 发送完毕 TCP

[编辑] 成员名称查找

类成员非限定及限定名称查找的规则详细列于名称查找

[编辑] 参阅