C++ return一个对象的执行细节
同学发过来一道考试题,要求解释输出。好久没有用过这些知识了,所以顺便复习下。
代码
为了搞清楚里面知识,所以把代码增加了地址的输出来进行实验:
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 63 64 65 66 67
| #include <limits.h> #include <stdio.h> #include <algorithm> #include <iostream> #include <map> #include <string> #include <utility> #include <vector>
using namespace std;
class Solution { public: Solution(): id_(0) { cout << "Default Constructor:id=" << id_ << " addr=:" << this << endl; } Solution(int id): id_(id) { cout << "Constructor with Paramter:id=" << id_ << " addr=:" << this << endl; } Solution(const Solution&course): id_(course.id_) { cout << "Copy Constructor :id=" << id_ << " addr=:" << this << endl; } ~Solution() { cout << "Destructor:id=" << id_ << " addr=:" << this << endl; } int GetId() {return id_;};
private: int id_; };
Solution Max(Solution a, Solution b) { if (a.GetId() > b.GetId()) { cout << "end " << endl; return a; cout << "end after return" << endl; } else { cout << "end " << endl; return b; cout << "end after return" << endl; } }
void Q2() { Solution class1(1); Solution class2 = class1; Solution classX(5), classY(6); Solution classZ; classZ = Max(classX, classY); cout << "after max" << endl; Solution *pClasses = new Solution[3]; delete [] pClasses; } int main(int argc, char const *argv[]) { Q2(); 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
| Constructor with Paramter:id=1 addr=:0x61fedc Copy Constructor :id=1 addr=:0x61fed8 Constructor with Paramter:id=5 addr=:0x61fed4 Constructor with Paramter:id=6 addr=:0x61fed0 Default Constructor:id=0 addr=:0x61fecc Copy Constructor :id=6 addr=:0x61fee4 Copy Constructor :id=5 addr=:0x61fee8 end Copy Constructor :id=6 addr=:0x61fee0 Destructor:id=6 addr=:0x61fee0 Destructor:id=5 addr=:0x61fee8 Destructor:id=6 addr=:0x61fee4 after max Default Constructor:id=0 addr=:0xfc1d74 Default Constructor:id=0 addr=:0xfc1d78 Default Constructor:id=0 addr=:0xfc1d7c Destructor:id=0 addr=:0xfc1d7c Destructor:id=0 addr=:0xfc1d78 Destructor:id=0 addr=:0xfc1d74 Destructor:id=6 addr=:0x61fecc Destructor:id=6 addr=:0x61fed0 Destructor:id=5 addr=:0x61fed4 Destructor:id=1 addr=:0x61fed8 Destructor:id=1 addr=:0x61fedc
|
可以看到输出结果与题目中的结果是有差别的,题目中的篮圈析构顺序是566,实验结果是656
解释
在调用Max之前的输出结果很容易理解
1 2 3 4 5
| Constructor with Paramter:id=1 addr=:0x61fedc # class1 的参数构造 Copy Constructor :id=1 addr=:0x61fed8 #Solution class2 = class1的拷贝构造 Constructor with Paramter:id=5 addr=:0x61fed4 #classX的参数构造 Constructor with Paramter:id=6 addr=:0x61fed0 #classY的参数构造 Default Constructor:id=0 addr=:0x61fecc #classZ的默认构造
|
进入Max函数后从右向左依次进行参数的压栈,所以首先是b的拷贝构造,然后是a的拷贝构造。
接下来是一个很重要的细节,在执行return的时候会自动执行一个拷贝构造来生成一个临时的对象temp用来返回。
1 2 3 4
| Copy Constructor :id=6 addr=:0x61fee4 #b的拷贝构造 Copy Constructor :id=5 addr=:0x61fee8 #a的拷贝构造 end Copy Constructor :id=6 addr=:0x61fee0 #return 产生的临时对象temp的拷贝构造
|
在Max函数中参数入栈的顺序是:先是b然后是a最后是temp。
需要注意的是,在执行完return之后并没有立即就执行了出栈的操作。需要在完成classZ = Max(classX, classY);
这个赋值语句后才会执行出栈操作对栈中的对象执行析构操作。析构的顺序就是出栈的顺序,先入后出,后入先出。所以首先是temp变量的析构,然后是b这个形参的析构,最后才是a这个形参的析构。
所以参考链接里面的顺序也是有误的,并不是首先析构形参。
1 2 3 4
| after max Default Constructor:id=0 addr=:0xfc1d74 # temp变量的析构 Default Constructor:id=0 addr=:0xfc1d78 # b这个形参的析构 Default Constructor:id=0 addr=:0xfc1d7c # a这个形参的析构
|
之后因为使用new来创建的数据,其内存是分配在堆上面的并没有在Max的函数栈中,需要手动delete来销毁来防止内存泄漏。因此首先是3个0个构造和析构。
1 2 3 4 5 6 7 8
| # new Default Constructor:id=0 addr=:0xfc1d74 Default Constructor:id=0 addr=:0xfc1d78 Default Constructor:id=0 addr=:0xfc1d7c #delete Destructor:id=0 addr=:0xfc1d7c Destructor:id=0 addr=:0xfc1d78 Destructor:id=0 addr=:0xfc1d74
|
最后是对Max这个函数中入栈的局部变量执行出栈与销毁的操作。
1 2 3 4 5
| Destructor:id=6 addr=:0x61fecc #classZ的析构 Destructor:id=6 addr=:0x61fed0 #classY的析构 Destructor:id=5 addr=:0x61fed4 #classX的析构 Destructor:id=1 addr=:0x61fed8 #class2的析构 Destructor:id=1 addr=:0x61fedc #class1的析构
|
总结
主要是两个点比较迷惑
- Max函数return 的操作是通过自动调用拷贝构造一个临时对象来执行的返回的,临时对象的析构需要等到
classZ = Max(classX, classY);
语句执行完毕后
- 如果先构造了一个对象,然后再使用=来进行赋值是不会执行拷贝构造的,不是只要使用了=就会执行拷贝构造。所以
Solution class2 = class1;
这个语句没有先构造对象,因此执行了拷贝构造。而classZ = Max(classX, classY);
这个语句中,classZ
事先用Solution classZ;
构造过,所以不会再执行拷贝构造的操作。这个=只负责值之间的传递。
参考
https://blog.csdn.net/fruitz/article/details/41624017 析构顺序有误