1.拷贝构造函数的&不可以去掉,因为如果去掉,从实参到形参又要多经过一次拷贝构造,会产生无限循环,编译不通过;拷贝赋值函数的&可以去掉,去掉就会先多调用一次拷贝构造函数(实参到形参);
2.二者的const都不是必须加的,但是加上更好,保证无法对原变量做任何修改;
3.格式:
MyClass(const Myclass& rhs) ------拷贝构造函数
{
//对于指针,浅拷贝直接赋值即可。
//深拷贝常见如对于const char* 的拷贝,先得到原串的长度,再开辟新数组空间,最后调用memcpy(目标地址,原地址,拷贝长度)来完成。
}
MyClass& operator = (const MyClass& rhs)
{
}
在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:
1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。
3.浅拷贝带来问题的本质在于析构函数释放多次堆内存,使用std::shared_ptr,可以完美解决这个问题。
4.移动构造函数:
移动ctor优化反反复复构造析构的性能问题:当函数返回一个临时对象而用一个左值承接时,要构造三次:函数内的对象、返回给左值的临时对象、左值;同样要析构三次,影响了性能。
1.当一个类实现了移动ctor且构造对象时传入了一个右值,则编译器会选用移动构造函数。
std::move()在实现上基本等同于类型转换static_cast<T&& >(lvalue);但是被转换值的生命周期并没有随着左右值的转变而改变。
2.移动ctor的类成员中如果有另一个类,需要将该成员同样转为右值,调用该成员的移动ctor;
3.移动ctor主要针对指针(堆内存),值类型成员正常赋值即可;
4.移动ctor处理指针类型成员,首先把rhs的指针成员进行赋值(地址所有权转移),然后就要把rhs的指针置空(移动构造完成后,临时对象就会立即被析构掉,如果不将指针置空,临时对象析构时就会将成员指针指向的内存释放。) ,相对的,拷贝ctor深拷贝指针类型成员时,会按照rhs指针所指的地址值新开辟一块内存,而非直接接管该内存的地址。
5.使用某个变量移动构造了另一变量,那么之后不可以再使用这个变量。
6.右值引用赋予临时变量以新的生命力,它们的生命周期将与该右值引用(它自身是一个左值)一致。
6.完美转发
当将一个左值传递给一个参数是右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参的左值引用。
可以传递一个左值int i给f,编译器将推断出T的类型为int&。再根据引用折叠规则 void f(int& &&)将推断为void f(int&),因此,f将被实例化为: void f<int&>(int&)。
move()、forward()可参考这篇文章: www.jianshu.com/p/b90d1091a…