当对象一般作为函数返回值时,一般会使用拷贝构造函数,然而在具体实现过程中,发现并没有通过拷贝构造函数来完成复制对象的工作,我们看一下例子。
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student(int age, string name)
{
this->age = age;
this->name = name;
}
Student (const Student& s)
{
cout<<"copy function"<<endl;
this->age = s.age;
this->name = s.name;
}
~Student()
{
cout<<"discard a object address: "<<this<<endl;
}
private:
int age;
string name;
};
Student student_say()
{
Student stu(10,"lmt");
cout<< "student in function address:"<<&stu<<endl;
return stu;
}
int main()
{
Student stu = student_say();
cout<<"student in main function address: "<<&stu<<endl;
return 0;
}
我们写了一个简单的类,并实现了拷贝构造函数,在使用拷贝构造函数处,会打印输出“copy function”。但这个程序的结果我们可以看一下:
函数里构建的对象地址和主函数里作为接受对象的地址一样,而且没有调用拷贝构造函数。why?
原因就在于编译器自行对其进行了优化,这里我们要了解函作为值返回时的操作。
- 创建Student类对象stu;
- main函数在栈额外开辟一片空间,并将这块空间的一部分作为传递返回值的临时对象,这里称为temp;
- temp完成对Student(stu)的拷贝构造 ( Student temp(stu)),函数把temp地址传出至eax寄存器;
- return返回以后,mian函数将eax 指向的temp对象的内容拷贝给main函数里的stu。
上述操作要完成三次对象的构造,函数内构造,栈内临时对象的构造,主函数里对象的构造,而函数内的构造和临时对象的构造会被析构,所以实际上只需一次构造即可,因此编译器优化后只是实现了一次构造函数。
但如果我们仍然想看函数返回值调用拷贝构造函数的情况的话,需要在函数内new一个对象,不过这里存在着内存泄漏的风险。
Student student_say()
{
Student *stu = new Student(10,"lmt");
cout<<"in function"<<endl;
cout<<"stu address"<<stu<<endl;
return *stu;
}
函数执行结果如下:
按照我们之前的思路就是,函数内构建对象,main函数栈内构建临时对象,临时对象完成对函数内对象的构造,并将地址传出至eax,在主函数里,将eax寄存器传出的地址内容拷贝到函数对象。但是我们会发现new出的对象我们没法完成对它的析构操作。正事因为在函数里构建的变量在栈上,因此堆上new出的对象无法对其析构,因此编译器无法对这种情况做出优化,因为我们可以看到拷贝函数被调用了。如果我们想析构这这个对象,函数就需要用通过引用返回,再通过传回的引用来析构该对象了。