首先写一个没有移动构造函数的类student
class student
{
public:
string name = "小红";
student() { cout << "student=>默认构造" << endl; }
student(string s) : name(s)
{
cout << "student=>有参构造" << endl;
};
student(const student &s) : name(s.name)
{
cout << "student=>拷贝" << endl;
}
student &operator=(const student &s)
{
this->name = s.name;
cout << "student=>赋值构造" << endl;
return *this;
}
~student() { cout << "student=>析构 : " << this->name << endl; }
void print()
{
cout << "student: " << name << endl;
}
};
写一个返回值为类对象的函数,此处返回值不以引用的形式返回,以局部变量的形式返回
student function1()
{
student s1("666");
return s1;
}
主函数中接收一下返回值
int main()
{
student s = function1();
}
我用的gcc编译器,由于正常编译时编译器会自动对构造函数进行优化(一会我们细说这个优化),这个优化会导致拷贝构造不会被调用,所以我们编译时加上下面的参数禁止编译器优化
-fno-elide-constructors
可以看到构造对象s时调用的是拷贝构造函数。
编译器之所以进行优化,因为拷贝构造函数会降低编译效率,增大内存占用,所以编译器默认进行了一个优化,这个优化就是直接使用s1的内存对s进行构造,这个方法有点类似于移动构造函数,那我们就手写一个移动构造函数加到类student里面看看效果
student(student &&s) : name(std::move(s.name))
{
cout << "student=>移动构造" << endl;
}
因为对象s1是局部变量,生命周期只在函数function1内,所以我们要使用右值引用的方式(student&& s)来延长对象内存的生命周期,说直白点,就是拿到一个对象,我只要这个对象的内存,不要这个对象本身,重新编译
可以看到,这次拷贝构造与移动构造都存在,优先调用了移动构造函数,这里编译的时候依然要禁止编译器优化,不然移动构造函数也不会被调用,那我可不可以认为编译器优化其实就是系统调用了一个默认的移动构造函数?由于我的水平有限,只能借用一下AI的内容了:
如果编译器使用返回值优化(Return Value Optimization, RVO)来避免调用拷贝构造函数或移动构造函数,那么它实际上是直接在目标对象的内存空间中构造了一个新对象,而不是通过移动构造函数来“移动”临时对象。
这种优化可以避免不必要的拷贝或移动操作,从而提高代码的性能。虽然这种优化的效果类似于移动构造函数,但它并不是通过调用移动构造函数来实现的。因此,如果你在类中定义了移动构造函数,并在其中添加了一些特殊的逻辑,那么这些逻辑将不会被执行。
总之,返回值优化(RVO)并不是通过调用移动构造函数来实现的,而是直接在目标对象的内存空间中构造了一个新对象。这种优化可以提高代码的性能,但它并不会调用你定义的移动构造函数。