#include
#include
using namespace std;
class Test{
private:
int i;
string s;
public:
Test():i(0),s(""){cout<<toString()<<"执行默认构造函数!"<<endl;}
Test(int i,string s):i(i),s(s){cout<<toString()<<"执行带参构造函数!"<<endl;}
Test(const Test &test):i(test.i),s(test.s){cout<<toString()<<"执行拷贝构造函数!"<<endl;}
~Test(){cout<<toString()<<"执行析构函数!"<<endl;}
Test operator+(const Test &test){
return Test(this->i+test.i,this->s+test.s);
}
string toString(){return "str:"+s+" "+"i="+to_string(i)+" ";}
};
Test getTest(){
Test test(3,"333");
cout<<"getTest()-->address:"<<&test<<endl;
return test;
}
int main(){
Test t1;
Test t2(2,"222");
Test t3 = t2;
Test t4 = getTest();
Test t5 = t3+t4;
cout<<"t4--->address:"<<&t4<<endl;
cout<<"t5--->address:"<<&t5<<endl;
}
- 执行命令:g++ -o test.o test.cpp; ./test.o后输出以下内容。
str: i=0 执行默认构造函数! // 第25行代码 Test t1; str:222 i=2 执行带参构造函数! // 第26行代码 Test t2(2,"222") str:222 i=2 执行拷贝构造函数! // 第27行代码 Test t3=t2 str:333 i=3 执行带参构造函数! // 第20行代码 Test test(3,"333") getTest()-->address:0x7ffd832adf20 // 打印函数getTest()中test的地址 str:222333 i=5 执行带参构造函数! // 第14行代码 return ***** t4--->address:0x7ffd832adf20 // 第30行 t5--->address:0x7ffd832adf50 // 第31行 str:222333 i=5 执行析构函数! // t5 str:333 i=3 执行析构函数! // t4 str:222 i=2 执行析构函数! // t3 str:222 i=2 执行析构函数! // t2 str: i=0 执行析构函数! // t1
思考:在调用getTest()函数的时候,根据所学的c++11标准,返回值应当首先拷贝到一个临时test变量中,然后将该临时的变量再拷贝给t4,所以应当在结果中的第5行之后调用拷贝构造函数给临时变量,再将该临时变量拷贝给t4。
同理Test t5=t3+t4也应当首先将operator+函数内的变量首先通过拷贝构造函数给一个临时变量,再将该临时变量拷贝给t5。
但是再结果中这些步骤均没有体现,并且通过getTest()返回的变量t4的地址与函数内地址相同,这似乎与返回一个引用无异。
通过搜索相关知识,了解到是由于ROV优化的结果。
- 执行指令:g++ -o test.o test.cpp -fno-elide-constructors; ./test.o后输出以下内容。其中新加入参数的作用是防止ROV优化。
str: i=0 执行默认构造函数! str:222 i=2 执行带参构造函数! str:222 i=2 执行拷贝构造函数! str:333 i=3 执行带参构造函数! getTest()-->address:0x7fff8b7ebf50 str:333 i=3 执行拷贝构造函数! // 可以推断出执行的位置是第22行 return test; str:333 i=3 执行析构函数! // 位置是第22行 return test; str:222333 i=5 执行带参构造函数! t4--->address:0x7fff8b7ec040 t5--->address:0x7fff8b7ec070 str:222333 i=5 执行析构函数! str:333 i=3 执行析构函数! str:222 i=2 执行析构函数! str:222 i=2 执行析构函数! str: i=0 执行析构函数!
思考:跟第一次不同的是增加了第5,6行的输出,在函数返回test时将test拷贝给一个临时变量,并将函数内的临时变量析构掉。可以看到t4与函数getTest内的test变量地址不一样了。
执行第28行时直接将该临时变量命名给t4而不是拷贝给t4。
同样在执行operator+直接将未命名的临时变量赋值给t5,没有拷贝给临时变量的过程。与所学的仍然有出入。
- 通过命令 info g++并搜索default发现默认的标准是c++17。因此执行以下指令:g++ -o test.o test.cpp -fno-elide-constructors -std=c++11; ./test.o 。新加参数指明使用c++11标准。输出如下:
str: i=0 执行默认构造函数! str:222 i=2 执行带参构造函数! str:222 i=2 执行拷贝构造函数! str:333 i=3 执行带参构造函数! getTest()-->address:0x7ffc4d62e0b0 str:333 i=3 执行拷贝构造函数! // 第22行 return test 将test拷贝给一个临时变量 str:333 i=3 执行析构函数! // 第22行 析构掉test str:333 i=3 执行拷贝构造函数! // 第28行 将临时变量拷贝给t4, str:333 i=3 执行析构函数! // 第28行,析构掉临时变量。 str:222333 i=5 执行带参构造函数!// 第14行 str:222333 i=5 执行拷贝构造函数!// 第14行,拷贝给一个临时变量 str:222333 i=5 执行析构函数! // 第14行,析构掉带参的构造函数构造的变量 str:222333 i=5 执行拷贝构造函数!// 第29行,将临时变量拷贝给t5 str:222333 i=5 执行析构函数! // 第29行,析构掉临时变量。 t4--->address:0x7ffc4d62e1a0 t5--->address:0x7ffc4d62e1d0 str:222333 i=5 执行析构函数! str:333 i=3 执行析构函数! str:222 i=2 执行析构函数! str:222 i=2 执行析构函数! str: i=0 执行析构函数!
思考:这次就完全按照c++11所教的流程执行了下来。
但是可以看到经过ROV优化后的步骤要少很多,避免了很多次构造函数和拷贝构造函数的调用。
参考:https://zhuanlan.zhihu.com/p/56008627
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)