C/C++教程

C++学习笔记 继承,虚基类

本文主要是介绍C++学习笔记 继承,虚基类,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一,派生类

省略访问说明符,关键词struct声明的类默认为public,关键词class声明的类为private

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

二,虚基类

对于每个指定virtual的不同基类,最终派生对象中仅含有该类型的一个基类子对象,(只要它每次都以 virtual 继承)。

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

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

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

允许空基类子对象大小为零

若空基类之一亦为首个非静态数据成员的类型或其类型的基类,则禁用空基优化,因为要求两个同类型基类子对象在最终派生类型的对象表示中必须拥有不同地址。

三,virtual函数说明符,虚函数

指定非静态成员函数为虚函数并支持动态调用派发,它只能在非 静态成员函数的首个声明(即当它与类定义中声明时)的声明说明符序列中出现

虚函数是可以在派生类中覆盖其行为的成员函数,与非虚函数相反,即使没有关于该类实际类型的编译时信息,仍然保留被覆盖的行为。

 1 #include <iostream>
 2 struct Base {
 3     virtual void f() {
 4         std::cout << "base\n";
 5     }
 6 };
 7 struct Derived : Base {
 8     void f() override { // 'override' 可选
 9         std::cout << "derived\n";
10     }
11 };
12 int main()
13 {
14     Base b;
15     Derived d;
16 
17     // 通过引用调用虚函数
18     Base& br = b; // br 的类型是 Base&
19     Base& dr = d; // dr 的类型也是 Base&
20     br.f(); // 打印 "base"
21     dr.f(); // 打印 "derived"
22 
23     // 通过指针调用虚函数
24     Base* bp = &b; // bp 的类型是 Base*
25     Base* dp = &d; // dp 的类型也是 Base*
26     bp->f(); // 打印 "base"
27     dp->f(); // 打印 "derived"
28 
29     // 非虚函数调用
30     br.Base::f(); // 打印 "base"
31     dr.Base::f(); // 打印 "base"
32 }

 

每个虚函数都有其最终覆盖函数,它是进行虚函数调用时所执行的函数。基类 Base 的虚成员函数 vf 是最终覆盖函数,除非派生类声明或(通过多重继承)继承了覆盖 vf 的另一个函数。

 1 struct A { virtual void f(); };     // A::f 是虚函数
 2 struct B : A { void f(); };         // B::f 覆盖 A::f in B
 3 struct C : virtual B { void f(); }; // C::f 覆盖 A::f in C
 4 struct D : virtual B {}; // D 不引入覆盖函数,B::f 在 D 中为最终
 5 struct E : C, D  {       // E 不引入覆盖函数,C::f 在 E 中为最终
 6     using A::f; // 非函数声明,仅令 A::f 能为查找所见
 7 };
 8 int main() {
 9    E e;
10    e.f();    // 虚调用调用 C::f,e 中的最终覆盖函数
11    e.E::f(); // 非虚调用调用 A::f,它在 E 中可见
12 }

若函数以说明符 override 声明,但不覆盖任何虚函数

若函数以说明符 final 声明,而另一函数试图覆盖之,

struct B {
    virtual void f(int);
};
struct D : B {
    virtual void f(int) override; // OK,D::f(int) 覆盖 B::f(int)
};


struct B {
    virtual void f() const final;
};
struct D : B {
    void f() const; // 错误:D::f 试图覆盖 final B::f
};

非成员函数和静态成员函数不能为虚函数。

函数模板不能被声明为 virtual。这只适用于自身是模板的函数——类模板的常规成员函数可被声明为虚函数。

虚析构函数

虽然析构函数是不继承的,但若基类声明其析构函数为 virtual,则派生的析构函数始终覆盖它。这使得可以通过指向基类的指针 delete 动态分配的多态类型对象

 1 class Base {
 2  public:
 3     virtual ~Base() { /* 释放 Base 的资源 */ }
 4 };
 5  
 6 class Derived : public Base {
 7     ~Derived() { /* 释放 Derived 的资源 */ }
 8 };
 9  
10 int main()
11 {
12     Base* b = new Derived;
13     delete b; // 进行对 Base::~Base() 的虚函数调用
14               // 由于它是虚函数,故它调用的是 Derived::~Derived(),
15               // 这就能释放派生类的资源,然后遵循通常的析构顺序
16               // 调用 Base::~Base()
17 }

四,抽象类

定义不能被实例化,但可以作基类的抽象类型

虚说明符=0

抽象类(abstract class)时定义或继承了至少一个最终覆盖函数为纯虚的函数的类,

抽象类型不能用形参类型,函数返回类型,或显示转换类型

可以声明到抽象类的指针或引用

 1 struct Abstract {
 2     virtual void f() = 0; // 纯虚
 3 }; // "Abstract" 为抽象
 4  
 5 struct Concrete : Abstract {
 6     void f() override {} // 非纯虚
 7     virtual void g();     // 非纯虚
 8 }; // "Concrete" 为非抽象
 9  
10 struct Abstract2 : Concrete {
11     void g() override = 0; // 纯虚覆盖函数
12 }; // "Abstract2" 为抽象
13  
14 int main()
15 {
16     // Abstract a; // 错误:抽象类
17     Concrete b; // OK
18     Abstract& a = b; // OK:到抽象基类的引用
19     a.f(); // 虚派发到 Concrete::f()
20     // Abstract2 a2; // 错误:抽象类(g() 的最终覆盖函数为纯虚)
21 }

override 说明符

指定一个虚函数覆盖另一个虚函数

 1 struct A
 2 {
 3     virtual void foo();
 4     void bar();
 5 };
 6  
 7 struct B : A
 8 {
 9     void foo() const override; // 错误:B::foo 不覆盖 A::foo
10                                // (签名不匹配)
11     void foo() override; // OK:B::foo 覆盖 A::foo
12     void bar() override; // 错误:A::bar 非虚
13 };

final 说明书

指定某个虚函数不能在子类中被覆盖,或者某个类不能被子类继承

 1 struct Base
 2 {
 3     virtual void foo();
 4 };
 5  
 6 struct A : Base
 7 {
 8     void foo() final; // Base::foo 被覆盖而 A::foo 是最终覆盖函数
 9     void bar() final; // 错误: bar 不能为 final 因为它非虚
10 };
11  
12 struct B final : A // struct B 为 final
13 {
14     void foo() override; // 错误:foo 不能被覆盖,因为它在 A 中是 final
15 };
16  
17 struct C : B // 错误:B 为 final
18 {
19 };

 

这篇关于C++学习笔记 继承,虚基类的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!