有的时候我们定义的某些类不需要拷贝构造函数和拷贝赋值运算符,比如iostream类就阻止拷贝,以避免多个对象写入或读取相同的IO缓冲。
新的标准里,我们可以在拷贝构造函数和拷贝赋值运算符函数的参数列表后面加上=delete用来指出我们希望将它定义为删除的,这样的函数称为删除函数。
class NoCopy { NoCopy() = default; // 使用合成的默认构造函数 NoCopy(const NoCopy&) = delete; // 删除拷贝 NoCopy& operator=(const NoCopy&) = delete; // 删除赋值 ~NoCopy() = default; // 使用合成的析构函数 };
注意:析构函数不能是删除的成员,因为这样的类是无法销毁的。
如果一个类有const成员或者有引用成员,则这个类合成拷贝赋值运算符是被定义为删除的。
在新的标准出来之前,类是通过将其拷贝构造函数的拷贝赋值运算符声明为private来阻止拷贝,而且为了防止成员被友元或其他成员访问,会对这些成员函数只声明,但不定义。
7,右值引用
所谓的右值引用就是必须绑定在右值上的引用,我们可以通过&&来获得右值引用,右值引用一个很重要的性质是只能绑定到一个将要销毁的对象,所以我们可以自由地将一个右值引用的资源“移动”到另一个对象中。
我们可以将一个右值引用绑定到表达式上,但不能将右值引用绑定到一个左值上:
int i = 42; int &r = i; // 正确:r引用i int &&rr = i; // 错误:不能将一个右值引用绑定到一个左值上 int &r2 = i * 42; // i*42是一具右值 const int& r3 = i * 42; // 可以将一个const的引用绑定到一个右值上 int && rr2 = i * 42; // 正确:将rr2绑定到乘法结果上
总体来说:左值有持久的状态,而右值要么是字面常量,要么是表达式求值过程中创建的临时对象。
从而我们得知,关于右值引用:1)所引用的对象将要销毁;2)该对象没有其他用户。
标准库提供了一个std::move函数,让我们可以获得左值上的右值引用:
int &&r3 = std::move(rr1); // rr1是一个变量
move调用告诉编译器:我们有一个左值,但是我们希望像一个右值一个处理它。在上面的代码后,要么销毁rr1,要么对rr1进行赋值,否则我们不能使用rr1。
另外一点值得注意的是,我们使用std::move而不是move,即使我们提供了using声明。
8,移动构造函数和移动赋值运算符
与拷贝一样,移动操作同样发生在我们一个类的对象去初始化或赋值同一个类类型的对象时,但是与拷贝不同的是,对象的内容实际上从源对象移动到了目标对象,而源对象丢失了内容。移动操作一般只发生在当这个源对象是一个uname的对象的时候。
一个uname object意思是一个临时对象,还没有被赋予一个名字,例如一个返回该类型的函数返回值或者一个类型转换操作返回的对象。
除特别注明外,本站所有文章均为 赢咖4注册 原创,转载请注明出处来自C++的那些事:类的拷贝控制