• 关于类之间赋值时生成零时对象并调用析构函数的时机


    一、生成临时对象

    看代码如下:

    #include 
    
    using namespace std;
    
    class B {
    public:
    	B() {
    		cout << "B  construction --- address is :" << this << endl;
    	}
    	~B() {
    		cout << "B destruction --- address is :" << this << endl;
    	}
    	B fool(const B& b1) {
    		cout << "fool func" << endl;
    		return b1;
    	}
    };
    
    int main() {
    	B b1, b2;
    	b1 = b1.fool(b2); // 21
    	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

    问在第21行结束后,会执行几次几次析构函数,是否会执行构造函数?

    	b1 = b1.fool(b2); // 21
    
    • 1

    先说答案:执行一次析构函数,析构的是给 b1赋值的临时变量
    加代码检验:
    在这里插入图片描述
    这里的return 0这句话还没执行,所以b1,和b2还没有执行析构,上面的代码中单单32行这段代码执行完毕之后,就有了下列输出:

    fool func
    复制构造函数 — address if003AFCFF
    operator=
    B destruction — address is :003AFCFF

    过程如下:

    1. 先跳转到B fool(const B& b1) {} 执行了cout << “fool func” << endl;
    2. 执行完return this之后,return给一个B类型的临时变量,将会把this通过复制构造函数传递给系统自动创建的临时变量,即跳转到:B(const B& b1){} 执行cout << “复制…” <
    3. 执行完复制构造函数以后,继续跳回32行,开始执行operator=,执行等号的重载B& operator=(const B& tem){},注意这里return *this 返回的是一个引用,直接将地址传给全局变量的b1,所以这里就不会出现第二步中,再传递给一个临时变量的情况。
    4. 上述重载执行完后继续跳转至32行,此时之前的操作都已经完毕,这时调试按F11,跳转至析构函数,执行第二步生成的临时变量的析构函数。
    5. 执行完以上步骤以后,跳转至return 0,即上图所示的情况。

    如果24行返回值类型是一个引用,则不会产生临时变量,因为直接引用了return *this的地址,将其直接转给b1,如下图:
    在这里插入图片描述
    但是返回局部变量的引用,并不是一个好习惯,这里还是没问题的,因为fool传尽力的就是全局变量的引用,并没有因为函数的结束而消失,如果是如下代码:

    B& fool(const B b1)
     {
     	cout << "fool func" << endl;
    	return *this;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    就会返回局部变量的引用 (形参b1的地址)b1在函数结束时就被释放。

    二、返回引用
    返回引用是返回一个地址,且指向该类的引用可以直接调用成员函数来修改成员变量,
    另外尽量不要返回局部对象的地址,因为当函数调用完成后,局部对象会被销毁,此时返回的地址不存在,进而导致后续产生错误。

    三、返回对象
    返回对象则是通过调用默认复制构造函数,来在另一地址重新生成了另一个对象。

    二、 构造函数与析构函数,在派生类、类成员与基类中的调用时机

    用下列代码来进行测试,A是B的基类,B的类成员中有一个C类的对象,在主函数中实例化一个B类的对象,并打上断点来查看机型顺序:

    #include 
    
    using namespace std;
    
    class A {
    public:
    	A() {
    		cout << "Base Constructor" << endl;
    	}
    	virtual ~A() {
    		cout << "Base Destruction" << endl;
    	}
    };
    
    class C {
    public:
    	C() {
    		cout << "Class Member Construction" << endl;
    	}
    	virtual ~C() {
    		cout << "Class Member Destruction" << endl;
    	}
    };
    
    class B : public A {
    public:
    	B() {
    		cout << "Derived Construction" << endl;
    	}
    	virtual ~B(){
    		cout << "Derived Destruction" << endl;
    	}
    	class C c;
    };
    
    
    int main() {
    	B b1;
    	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

    在38行打上断点以后,函数的执行顺序为:
    38->27->7 8(Base Con) 9-> 27 -> 17 18(Member Con) 19 -> 27 28(Derived Con) 29->39
    以上为主函数中第38行执行的内容,输出如下:
    在这里插入图片描述
    继续执行39行
    39-> 30 31(Derived Des) 32-> 20 21(Member Des) 22 -> 32 -> 10 11(Base Des) 12 -> 32 -> 39 ->40
    以上为主函数39行(return 0)所析构的内容顺序:
    在这里插入图片描述

    说明了派生类中构造函数的执行顺序:

    1. 基类的构造函数
    2. 类类成员的构造函数
    3. 派生类自身的构造函数

    析构循序倒过来即可。

    以上几点基本问题的补充:

    1. 构造函数不能为虚函数
      构建对象时,首先,分配一块内存;其次,调用构造函数。如果构造函数是虚函数,那么就需要通过vtable 来调用,但此时面对一块 raw memeory,到哪里去找 vtable 呢?毕竟,vtable 是在构造函数中才初始化的啊,而不是在其之前。因此构造函数不能为虚函数。
    2. 析构函数可以写为虚函数,且常常如此
      此时 vtable 已经初始化了;况且我们通常通过基类的指针来销毁对象,如果析构函数不为虚的话,就不能正确识别对象类型,从而不能正确销毁对象。
  • 相关阅读:
    49位主播带货破亿,单品直播销量100万+,9月的黑马都是谁?
    Vuex安装以及简单封装
    基于Java的校园跑腿接单管理系统设计与实现(源码+lw+部署文档+讲解等)
    三台centos7部署redis6.2版本集群
    spring boot + vue 前后端下载文件文件
    Java并发常见面试题(一)
    如何破解安全难题?另一个商用车ADAS市场窗口开启
    PaddleOCR 自制模型训练
    java 小练习
    【Effective_Objective-C_1熟悉Objective_C】
  • 原文地址:https://blog.csdn.net/Heck_Jacke/article/details/126647311