一、产生临时对象的情况和解决方案
1、以传值的方式给函数传递参数
一般以传引用的方式来传递参数,可以少调一次构造函数和析构函数
2、类型转换生成的临时对象/隐式类型转换以保证函数调用成功
类型转换生成的临时对象
class Element { int m_val; Element(int val = 10) : m_val(val) { } } Element ele; // 调用一次默认构造函数 ele = 100; // 调用一次构造函数, 调用一次拷贝赋值函数,调用析构函数 // 说明这句话会先构造一个临时变量,再将临时变量拷贝给ele Element ele = 100; // 只会调用一次构造函数,直接用100来构造ele对象,构造在ele的预留空间中,不会生成临时对象,
隐式类型转换以保证函数调用成功
void Func(Element& ele); void Func2(const Element& ele); Func(100); // 编译失败 Func2(100); // 编译成功,系统会将100转换成一个临时对变量,再传给函数Func2 // C++只会为const引用隐式类型转换产生临时变量,而不会为非const引用产生临时变量
3、函数返回对象
Element Func() // 该函数返回temp时,调用拷贝构造函数构造一个临时变量 { Element temp; // 调用了一次构造函数 return temp; // 返回时会调用一次拷贝构造函数 } Func(); // 当临时变量没有被接住的时候,会自动释放掉 Element ele = Func(); // 当临时变量被变量接住时,拷贝构造函数会直接在ele的预留空间中进行构造 // 函数优化。如果函数满足优化条件,即可以直接返回构造函数 Element Func() { return Element(); // 直接返回构造函数, // 这样操作只会调用一次构造函数来构造临时变量,不会调用拷贝构造函数,节省了一次拷贝和临时变量的析构 }
二、移动构造函数
// 格式: Element(Element && elem) noexcept : 初始化列表 // noexcept:通知标准库我们这个移动构造函数不抛出任何异常(提高编译器效率),一般情况下,移动构造函数都要加这个关键字,可以提高编译器效率 // 当用户自定义了移动构造函数之后,原来函数返回值的临时变量调用拷贝构造函数的情况,都会自动调用移动构造函数,但是具体操作需要自己实现
三、移动赋值运算符
Element& operate=(Element && elem) noexcept // 也需要加noexcept 关键字 // 拿到对方内存的时候,需要将对方的指针置空
四、哪些情况下系统会帮我们合成移动构造函数和移动赋值运算符
1、不会!当有自己的拷贝构造函数、拷贝赋值运算符或者析构函数(三者主要有其一),编译器将不会帮我们合成移动构造函数和移动赋值运算符
所以有些类没有移动构造函数和移动赋值运算符。
2、如果没有自己的移动构造函数和移动赋值运算符,那么系统会调用我们自己写的拷贝构造函数和拷贝赋值运算符。
3、只有一个类没有定义自己的拷贝构造函数、拷贝赋值运算符或者析构函数。且每个非静态成员都可以移动时,编译器才会为该类合成移动构造函数和移动赋值运算符。
什么叫可以移动:
(1)内置类型是可以移动的
(2)类类型,则这个类要有对应的移动操作相关的函数,就可以移动。
五、移动构造函数小结
(1)尽量给类增加移动构造和移动赋值运算符
(2)再不需要抛出异常的情况下一定要加 noexcept 关键字,保证该执行移动构造函数的时候不会去执行拷贝构造函数
(3)移动构造函数执行后,一定要记得给移动后的指针置空
原文:https://www.cnblogs.com/zhiminzeng/p/13127428.html