• 【C++】继承 ⑦ ( 继承中的对象模型分析 | 继承中的构造函数和析构函数 )







    一、继承中的对象模型分析




    1、继承代码示例


    下面有 3 个类 , 分别是 A 类 , B 类 , C 类 ;

    • A 类是 基类 ;
    • B 类 公有继承 A 类 , 并定义了新的 成员变量 y ;
    • C 类 公有继承 B 类 , 并定义了新的 成员变量 z ;
    class A {
    public:
    	int x;
    };
    
    class B : public A {
    public:
    	int y;
    };
    
    class C : public B {
    public:
    	int z;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    分别定义上述 3 个类的对象 ,

    	A objA;
    	B objB;
    	C objC;
    
    • 1
    • 2
    • 3

    2、基类与派生类内存模型


    上述 3 个对象的内存模型如下 :

    在这里插入图片描述

    • A 类对象 objA 中有一个成员 int x , 在内存中只有一个 int 类型的空间 ;
    • B 类对象 objB 中 , 除了继承自 A 类的 int x 成员 , 还有一个自己的 int y 成员 , 在内存中是 2 个 int 类型的空间 ;
    • C 类对象 objC 中 , 除了继承自 B 类的 int x 和 int y 成员 , 还有一个自己的 int z 成员 , 在内存中是 3 个 int 类型的空间 ;

    3、问题引入 - 派生类对象构造函数和析构函数调用


    上述 继承 的过程中 , 每一层继承 , 都继承了上一级 父类的 成员变量 , 同时自己也定义了新的成员变量 ;

    • 在 派生类对象 构造时 , 构造函数如何进行调用 ;
    • 在 派生类对象 析构时 , 析构函数如何进行调用 ;

    本篇博客开始讨论上述问题 ;


    4、完整代码示例 - 派生类对象内存模型


    #include "iostream"
    using namespace std;
    
    class A {
    public:
    	int x;
    };
    
    class B : public A {
    public:
    	int y;
    };
    
    class C : public B {
    public:
    	int z;
    };
    
    int main() {
    
    	A objA;
    	B objB;
    	C objC;
    	
    
    	// 控制台暂停 , 按任意键继续向后执行
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30




    二、继承中的构造函数和析构函数




    1、子类构造函数与析构函数调用顺序


    继承中的构造函数和析构函数 :

    • 子类构造 : 子类对象 进行 构造 时 , 需要调用 父类 的 构造函数 对 继承自父类的 成员变量 进行 初始化 操作 ; 构造函数 调用顺序如下 :
      • 构造时 , 先调用 父类 的构造函数 , 构造继承自父类的成员 ;
      • 然后 , 再调用 子类 的 构造函数 , 构造 子类 自己定义的成员 ;
    • 子类析构 : 子类对象 进行 析构 时 , 需要调用 父类 的 析构函数 对 继承自父类的 成员变量 进行 析构 操作 ; 析构函数调 用顺序如下 :
      • 析构时 , 先 调用 子类 的 析构函数 , 析构 子类 自己的成员 ;
      • 然后 , 再调用 父类 的 析构函数 , 析构 继承自父类的成员 ;

    2、子类构造函数参数列表


    如果 父类 的 构造函数 有 参数 , 则 需要再 子类 的 初始化列表中 显示调用 该有参构造函数 ;

    如果 A 类有构造函数 :

    class A {
    public:
    	A(int a)
    	{
    		this->x = a;
    		cout << "A 构造函数调用" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    B 类 如果继承 A 类 ,

    • 如果 A 类有默认的构造函数 , B 类的构造函数可以这么写 , 不显式调用 A 类的构造函数 , 默认调用 A 类的 无参 默认构造函数 ;
    class B : public A {
    public:
    	B(int b)
    	{
    		this->y = b;
    		cout << "B 构造函数调用" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 如果 A 类 没有 默认的构造函数 , B 类的构造函数 必须 显式调用 A 类的构造函数 , 并传入 A 类的构造函数参数 ;
    class B : public A {
    public:
    	B(int a, int b) : A(a)
    	{
    		this->y = b;
    		cout << "B 构造函数调用" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、代码示例 - 继承中的构造函数和析构函数


    代码示例 :

    #include "iostream"
    using namespace std;
    
    class A {
    public:
    	A(int a)
    	{
    		this->x = a;
    		cout << "A 构造函数调用" << endl;
    	}
    	~A()
    	{
    		cout << "A 析构函数调用" << endl;
    	}
    public:
    	int x;
    };
    
    class B : public A {
    public:
    	B(int a, int b) : A(a)
    	{
    		this->y = b;
    		cout << "B 构造函数调用" << endl;
    	}
    	~B()
    	{
    		cout << "B 析构函数调用" << endl;
    	}
    public:
    	int y;
    };
    
    class C : public B {
    public:
    	C(int a, int b, int c) : B(a, b)
    	{
    		this->z = c;
    		cout << "C 构造函数调用" << endl;
    	}
    	~C()
    	{
    		cout << "C 析构函数调用" << endl;
    	}
    public:
    	int z;
    };
    
    int main() {
    
    	C obj(1, 2, 3);
    
    	cout << "obj.x = " << obj.x << 
    		 " , obj.y = " << obj.y << 
    		 " , obj.z = " << obj.z << endl;
    	
    
    	// 控制台暂停 , 按任意键继续向后执行
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    执行结果 :

    A 构造函数调用
    B 构造函数调用
    C 构造函数调用
    obj.x = 1 , obj.y = 2 , obj.z = 3
    请按任意键继续. . .
    C 析构函数调用
    B 析构函数调用
    A 析构函数调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

  • 相关阅读:
    前端面试的话术集锦第 20 篇博文——高频考点(输入 URL 到页面渲染的整个流程)
    【Spring Cloud】多数据源配置
    【Go并发编程】Goroutine的基本使用
    android全局捕获异常错误
    梁建章:旅行重回全球时代主题 构建“创新与传承”大场景
    mysql load data infile导入数据主键重复怎么解决
    ES6-新增方法
    MongoDB - 聚合查询
    Android 缩短开机动画的播放时长
    TypeError: %c requires int or char
  • 原文地址:https://blog.csdn.net/han1202012/article/details/133939550