本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:programming:cpp:courses:cpp_basic_deep:chpt_13 [2024/11/18 03:59] – [纯虚函数] codinghare | cs:programming:cpp:courses:cpp_basic_deep:chpt_13 [2024/11/20 13:01] (当前版本) – [using 与重写] codinghare | ||
---|---|---|---|
行 323: | 行 323: | ||
* 析构:先析构派生类成员 | * 析构:先析构派生类成员 | ||
====补充知识==== | ====补充知识==== | ||
+ | ===继承方式的影响=== | ||
+ | 影响的实际是被继承成员的访问权限: | ||
+ | * '' | ||
+ | * 实际上代表了 '' | ||
+ | * '' | ||
+ | * 实际上代表了派生类的实现是基于基类的这样一种关系 | ||
+ | * '' | ||
+ | ===using 与继承=== | ||
+ | * 使用 '' | ||
+ | <code cpp> | ||
+ | struct Base | ||
+ | { | ||
+ | public: | ||
+ | int pub; | ||
+ | private: | ||
+ | int pri; | ||
+ | protected: | ||
+ | int pro; | ||
+ | }; | ||
+ | |||
+ | struct Derived: public Base | ||
+ | { | ||
+ | // inaccessiable 的成员(父类 Private 成员)无法修改权限 | ||
+ | public: | ||
+ | // 修改继承的 Protected 成员权限为 public | ||
+ | using Base::pro; | ||
+ | private: | ||
+ | // 修改继承的 public 成员权限为 为 private | ||
+ | using Base::pub; | ||
+ | }; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Derived d; | ||
+ | // 修改后可访问 | ||
+ | d.pro; | ||
+ | // 修改后无法访问 | ||
+ | d.pub; | ||
+ | } | ||
+ | </ | ||
+ | <WRAP center round box 100%> | ||
+ | 对派生类不可见的成员(基类中的 '' | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==using 与成员函数== | ||
+ | * '' | ||
+ | <code cpp> | ||
+ | // 在派生类中使用 | ||
+ | using Base::func; | ||
+ | </ | ||
+ | * 引入的是**函数名**:所有同名的函数都会被引入,在派生类中通过函数匹配规则来调用:比如上面的例子中: | ||
+ | <code cpp> | ||
+ | // 所有名字为 func 的函数都会被引入派生类 | ||
+ | void func(int); | ||
+ | void func(int, | ||
+ | // 派生类中根据函数匹配调用 | ||
+ | // ... | ||
+ | Derived d; | ||
+ | d.func(1); // 调用 func(int) | ||
+ | d.func(2, | ||
+ | </ | ||
+ | * 如果派生类中存在同名函数的定义,那么会**隐藏**所有引入的,基类的同名函数: | ||
+ | <code cpp> | ||
+ | sturct Derived : public Base | ||
+ | { | ||
+ | void func(int, int, int) {//...} | ||
+ | }; | ||
+ | // 错误,无法找到匹配函数 | ||
+ | d.func(1); | ||
+ | </ | ||
+ | <WRAP center round box 100%> | ||
+ | 这点同样适应于构造函数。通常情况下,如果派生类中没有引入新的数据成员,那么可以使用 '' | ||
+ | 但当派生类中定义了构造函数时,派生类的初始化则会调用派生类的构造函数。两者达到的效果相同,但的路径不同: | ||
+ | * '' | ||
+ | * 派生类中定义了构造函数时:通过 '' | ||
+ | </ | ||
+ | ==using 与重写== | ||
+ | * '' | ||
+ | * 如果希望使用 '' | ||
+ | * 最好的办法是使用虚函数的特性,对**特定的重载**进行重写(带函数签名的,比如 '' | ||
+ | * 也可以通过在派生类中使用函数隐藏来进行重写 | ||
+ | ===基类指针与容器=== | ||
+ | C++ 可以通过多态(基类指针)可以(有限)实现容器存储以及访问不同类型的对象: | ||
+ | <code cpp> | ||
+ | struct Base | ||
+ | { | ||
+ | // 访问内容函数 | ||
+ | virtual double getValue() = 0; | ||
+ | // 使用基类指针访问派生类时,释放堆资源必须声明虚析构 | ||
+ | virtual ~Base() = default; | ||
+ | }; | ||
+ | |||
+ | struct DerivedI: public Base | ||
+ | { | ||
+ | DerivedI(int x):val(x) {} | ||
+ | // 注意这里的 double,限制在这里 | ||
+ | double getValue() override { return val; }; | ||
+ | int val; | ||
+ | }; | ||
+ | |||
+ | struct DerivedD: public Base | ||
+ | { | ||
+ | DerivedD(double x): val(x) {} | ||
+ | double getValue() override { return val; }; | ||
+ | double val; | ||
+ | }; | ||
+ | int main(int argc, char const *argv[]) | ||
+ | { | ||
+ | // 使用智能指针作为基类指针 | ||
+ | std:: | ||
+ | | ||
+ | // 使用 vec 通过 new 返回的指针,存储 Base 的不同派生对象 | ||
+ | vec.emplace_back(new DerivedI(1)); | ||
+ | vec.emplace_back(new DerivedD(3.14)); | ||
+ | | ||
+ | for (auto &obj : vec) | ||
+ | { | ||
+ | std::cout << obj-> | ||
+ | } | ||
+ | std::cout << std::endl; | ||
+ | return 0; | ||
+ | }; | ||
+ | </ | ||
+ | <WRAP center round box 100%> | ||
+ | 可以看出来这种实现是有局限性的:虚函数返回的是派生类的公共类型:'' | ||
+ | 那么这种实现也是不可能的。 | ||
+ | </ | ||
+ | ===多重继承与虚继承=== | ||
+ | * 虚继承:以 '' | ||
+ | <code cpp> | ||
+ | Class D1 : virtual public Base { .... }; | ||
+ | </ | ||
+ | * 解决的问题:菱形继承带来的数据成员重复的问题,保证最终继承者的数据成员不会因为多重继承而翻倍。 | ||
+ | ===空基类优化=== | ||
+ | ==空类的大小为 1== | ||
+ | 空类的大小被定义为 '' | ||
+ | <code cpp> | ||
+ | ClassType a[2]; | ||
+ | a[1] -> a[0 + 1] -> address(a[0]) + 1 * sizeof(ClassType) | ||
+ | </ | ||
+ | 这种情况下,如果空类大小为 '' | ||
+ | ==空类的问题以及传统解决方案== | ||
+ | 有几个前提条件: | ||
+ | * 成员函数不占用类的空间 | ||
+ | * 根据计算机的不同,类中元素占用空间不足字的,会进行内存对齐:比如空类 '' | ||
+ | 根据上述信息,因为这个 '' | ||
+ | 进行继承。在这种情况下,编译器会进行空基类优化,忽略空基类的大小: | ||
+ | <code cpp> | ||
+ | struct Base { // some funcs ... }; // empty class | ||
+ | |||
+ | // obj = 4 bytes | ||
+ | struct Derived1 : Base | ||
+ | { | ||
+ | int i; | ||
+ | }; | ||
+ | </ | ||
+ | == C++20 的解决方案== | ||
+ | 上述解决方案的问题在于,public 继承的意义是描述 //is-a// 关系,但明显该类关系不是。C++ 20 提供了一种 '' | ||
+ | 用于描述空类。被该类类型定义的空类大小为 '' | ||
+ | |||
+ | <code cpp> | ||
+ | struct Empty {}; // empty class | ||
+ | |||
+ | struct X | ||
+ | { | ||
+ | int i; | ||
+ | [[no_unique_address]] Empty e; | ||
+ | }; | ||
+ | </ |