第十三章 拷贝控制(Chapter XIII copy control)

转载自https://github.com/applenob/Cpp_Primer_Practice,看C++primer的时用的笔记。自己做了一些补充,感谢前人的总结!
这章比较难,初学C++的可以结合《Essential C++》的第五章学习。回过头来再看这一章会轻松一点。

拷贝控制操作(copy control):

  • 拷贝构造函数(copy constructor)
  • 拷贝赋值运算符(copy-assignment operator)
  • 移动构造函数(move constructor)
  • 移动赋值函数(move-assignement operator)
  • 析构函数(destructor)

拷贝、赋值和销毁

拷贝构造函数

  • 如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
  • class Foo{ public: Foo(const Foo&); }
  • 合成的拷贝构造函数(synthesized copy constructor):会将参数的成员逐个拷贝到正在创建的对象中。
  • 拷贝初始化:

    将右侧运算对象拷贝到正在创建的对象中,如果需要,还需进行类型转换。
    通常使用拷贝构造函数完成。
    string book = “9-99”;
    出现场景:

    用=定义变量时。
    将一个对象作为实参传递给一个非引用类型的形参。
    从一个返回类型为非引用类型的函数返回一个对象。
    用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。

  • 将右侧运算对象拷贝到正在创建的对象中,如果需要,还需进行类型转换。
  • 通常使用拷贝构造函数完成。
  • string book = “9-99”;
  • 出现场景:

    用=定义变量时。
    将一个对象作为实参传递给一个非引用类型的形参。
    从一个返回类型为非引用类型的函数返回一个对象。
    用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。

  • 用=定义变量时。
  • 将一个对象作为实参传递给一个非引用类型的形参。
  • 从一个返回类型为非引用类型的函数返回一个对象。
  • 用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。

拷贝赋值运算符

  • 重载赋值运算符:

    重写一个名为operator=的函数.
    通常返回一个指向其左侧运算对象的引用。
    Foo& operator=(const Foo&);

  • 重写一个名为operator=的函数.
  • 通常返回一个指向其左侧运算对象的引用。
  • Foo& operator=(const Foo&);
  • 合成拷贝赋值运算符:

    将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。

  • 将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。

析构函数

  • 释放对象所使用的资源,并销毁对象的非static数据成员。
  • 名字由波浪号接类名构成。没有返回值,也不接受参数。
  • ~Foo();
  • 调用时机:

    变量在离开其作用域时。
    当一个对象被销毁时,其成员被销毁。
    容器被销毁时,其元素被销毁。
    动态分配的对象,当对指向它的指针应用delete运算符时。
    对于临时对象,当创建它的完整表达式结束时。

  • 变量在离开其作用域时。
  • 当一个对象被销毁时,其成员被销毁。
  • 容器被销毁时,其元素被销毁。
  • 动态分配的对象,当对指向它的指针应用delete运算符时。
  • 对于临时对象,当创建它的完整表达式结束时。
  • 合成析构函数:

    空函数体执行完后,成员会被自动销毁。
    注意:析构函数体本身并不直接销毁成员。

  • 空函数体执行完后,成员会被自动销毁。
  • 注意:析构函数体本身并不直接销毁成员。

三/五法则

  • 需要析构函数的类也需要拷贝和赋值操作。
  • 需要拷贝操作的类也需要赋值操作,反之亦然。

使用=default

  • 可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本。
  • 合成的函数将隐式地声明为内联的。

阻止拷贝

  • 大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符,无论是隐式地还是显式地。
  • 定义删除的函数:=delete。
  • 虽然声明了它们,但是不能以任何方式使用它们。
  • 析构函数不能是删除的成员。
  • 如果一个类有数据成员不能默认构造、拷贝、复制或者销毁,则对应的成员函数将被定义为删除的。
  • 老版本使用private声明来阻止拷贝。

拷贝控制和资源管理

  • 类的行为可以像一个值,也可以像一个指针。

    行为像值:对象有自己的状态,副本和原对象是完全独立的。

    行为像指针:共享状态,拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。

    赋值运算符
    //赋值运算符组合了析构函数和拷贝构造函数的工作,先在底层生成对象空间,删除类内原有的内存空间,然后指向新分配的指针。
    HasPtr& operator=(const HasPtr& hp){
    auto new_p=new std::string (*hp.ps);//拷贝底层string
    delete ps;//释放旧内存
    ps=new_p;//从右侧运算对象拷贝数据到本对象
    i=hp.i;
    return *this;//返回本对象
    }

  • 行为像值:对象有自己的状态,副本和原对象是完全独立的。
  • 行为像指针:共享状态,拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。

赋值运算符

交换操作

  • 管理资源的类通常还定义一个名为swap的函数。
  • 经常用于重排元素顺序的算法。
  • 用swap而不是std::swap。

对象移动

  • 很多拷贝操作后,原对象会被销毁,因此引入移动操作可以大幅度提升性能。
  • 在新标准中,我们可以用容器保存不可拷贝的类型,只要它们可以被移动即可。
  • 标准库容器、string和shared_ptr类既可以支持移动也支持拷贝。IO类和unique_ptr类可以移动但不能拷贝。

右值引用

  • 新标准引入右值引用以支持移动操作。
  • 通过&&获得右值引用。
  • 只能绑定到一个将要销毁的对象。
  • 常规引用可以称之为左值引用。
  • 左值持久,右值短暂。

move函数

  • int &&rr2 = std::move(rr1);
  • move告诉编译器,我们有一个左值,但我希望像右值一样处理它。
  • 调用move意味着:除了对rr1赋值或者销毁它外,我们将不再使用它。

移动构造函数和移动赋值运算符

  • 移动构造函数:

    第一个参数是该类类型的一个引用,关键是,这个引用参数是一个右值引用。
    StrVec::StrVec(StrVec &&s) noexcept{}
    不分配任何新内存,只是接管给定的内存。

  • 第一个参数是该类类型的一个引用,关键是,这个引用参数是一个右值引用。
  • StrVec::StrVec(StrVec &&s) noexcept{}
  • 不分配任何新内存,只是接管给定的内存。
  • 移动赋值运算符:

    StrVec& StrVec::operator=(StrVec && rhs) noexcept{}

  • StrVec& StrVec::operator=(StrVec && rhs) noexcept{}

合成的移动操作:

只有当一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会为它合成移动构造函数或移动赋值运算符。如中有移动操作,就可以生成合成的移动操作。

string

如果显示的要求编译器生成=default的移动操作,且编译器不能移动所有成员时,编译器会将移动操作定义为删除的函数。

一个类是否定义了自己的移动操作对拷贝操作如何合成有影响。如果类定义了一个移动构造函数和/或一个移动赋值运算符,则该类的合成拷贝构造函数和拷贝赋值运算符会被定义为删除的。

  • 移动右值,拷贝左值。
  • 如果没有移动构造函数,右值也被拷贝。
  • 更新三/五法则:如果一个类定义了任何一个拷贝操作,它就应该定义所有五个操作。
  • 移动迭代器:通过改变给定迭代器的解引用运算符的行为来适配此迭代器

    make_move_iterator函数讲一个普通迭代器转换为一个移动迭代器。

  • make_move_iterator函数讲一个普通迭代器转换为一个移动迭代器。
  • 建议:小心地使用移动操作,以获得性能提升。
  • 在编写移动操作时,必须确保以后原对象进入一个可析构的状态

右值引用和成员函数

  • 区分移动和拷贝的重载函数通常有一个版本接受一个const T&,而另一个版本接受一个T&&。
  • 引用限定符:

    在参数列表后面防止一个&,限定只能向可修改的左值赋值而不能向右值赋值。

  • 在参数列表后面防止一个&,限定只能向可修改的左值赋值而不能向右值赋值。
Foo& operator=(const Foo&) &;//防止对右值赋值

引用限定符可以是&和&&,对于&限定的函数,我们只能将它用于左值;对于&&限定的函数,只能用于右值:

Foo &retFoo();//返回一个引用;retFoo调用是一个左值
Foo retVal();//返回一个值,retval调用是一个右值
Foo j;

retFoo()=j; //正确,retfoo返回一个左值
retVal()=j; //错误,retval()返回一个右值
j=retval(); //正确,我们可以将一个右值作为赋值操作的右侧运算对象

一个函数可以同时用const和引用限定,引用限定符&必须跟随在const限定符后。

当定义const成员函数时,可以定义两个版本,唯一的差别是一个版本有const限定而另一个没有。引用限定的函数不同。如果定义两个或两个以上相同名字和相同参数列表的成员函数,就必须对所有函数都加上引用限定符,或者都不加。

————————

Reprinted from https://github.com/applenob/Cpp_Primer_Practice , look at the notes used in C + + primer. I have made some supplements. Thank you for your summary!
This chapter is difficult. Beginners of C + + can learn it in combination with Chapter 5 of essential C + +. It will be easier to look back at this chapter.

< strong > copy control operation < / strong > (copy control):

  • 拷贝构造函数(copy constructor)
  • 拷贝赋值运算符(copy-assignment operator)
  • 移动构造函数(move constructor)
  • 移动赋值函数(move-assignement operator)
  • 析构函数(destructor)

Copy, assign and destroy

copy constructor

  • If the first parameter of a constructor is a reference to its own class type and any additional parameters have default values, the constructor is a copy constructor.
  • class Foo{ public: Foo(const Foo&); }
  • Synthesized copy constructor: copies the members of the parameters one by one into the object being created.
  • Copy initialization:
    Copy the right operand to the object being created. If necessary, type conversion is also required.
    This is usually done using the copy constructor.
    string book = “9-99”;
    Scenario:
    When defining variables with =.
    Pass an object as an argument to a formal parameter of a non reference type.
    Returns an object from a function whose return type is a non reference type.
    Initializes elements in an array or members in an aggregate class with a curly brace list.
  • Copy the right operand to the object being created. If necessary, type conversion is also required.
  • This is usually done using the copy constructor.
  • string book = “9-99”;
  • Scenario:
    When defining variables with =.
    Pass an object as an argument to a formal parameter of a non reference type.
    Returns an object from a function whose return type is a non reference type.
    Initializes elements in an array or members in an aggregate class with a curly brace list.
  • When defining variables with =.
  • Pass an object as an argument to a formal parameter of a non reference type.
  • Returns an object from a function whose return type is a non reference type.
  • Initializes elements in an array or members in an aggregate class with a curly brace list.

copy assignment operator

  • Overload assignment operator:
    Rewrite a function named operator =
    Usually returns a reference to the operand to its left.
    Foo& operator=(const Foo&);
  • Rewrite a function named operator =
  • Usually returns a reference to the operand to its left.
  • Foo& operator=(const Foo&);
  • Composite copy assignment operator:
    Assign each non static member of the right operand to the corresponding member of the left operand.
  • Assign each non static member of the right operand to the corresponding member of the left operand.

Destructor

  • Release the resources used by the object and destroy the non static data members of the object.
  • The name consists of a tilde followed by a class name. No return value and no parameters are accepted.
  • ~Foo();
  • Call time:
    When a variable leaves its scope.
    When an object is destroyed, its members are destroyed.
    When a container is destroyed, its elements are destroyed.
    Dynamically allocated object when the delete operator is applied to the pointer to it.
    For a temporary object, when the complete expression that created it ends.
  • When a variable leaves its scope.
  • When an object is destroyed, its members are destroyed.
  • When a container is destroyed, its elements are destroyed.
  • Dynamically allocated object when the delete operator is applied to the pointer to it.
  • For a temporary object, when the complete expression that created it ends.
  • Synthetic destructor:
    After the empty function body is executed, the members will be automatically destroyed.
    Note: the destructor body itself does not directly destroy members.
  • After the empty function body is executed, the members will be automatically destroyed.
  • Note: the destructor body itself does not directly destroy members.

Three / five rule

  • Classes that need destructors also need copy and assignment operations.
  • Classes that need copy operations also need assignment operations, and vice versa.

使用=default

  • You can explicitly require the compiler to generate a composite version by defining the copy control member as = default.
  • The synthesized function will be implicitly declared inline.

Block copy

  • Most classes should define default constructors, copy constructors, and copy assignment operators, either implicitly or explicitly.
  • Define the function to delete: = delete.
  • Although they are declared, they cannot be used in any way.
  • Destructors cannot be deleted members.
  • If a class has data members that cannot be constructed, copied, copied or destroyed by default, the corresponding member function will be defined as deleted.
  • Older versions used private declarations to block copies.

Copy control and resource management

  • A class can behave like a value or a pointer.
    Behavior image value: the object has its own state, and the copy is completely independent of the original object.
    Behavior is like a pointer: shared state. When copying an object of this kind, the copy uses the same underlying data as the original object.
    Assignment Operators
    //The assignment operator combines the work of the destructor and the copy constructor. First, generate the object space at the bottom, delete the original memory space in the class, and then point to the newly allocated pointer.
    HasPtr& operator=(const HasPtr& hp){
    auto new_ p=new std::string (*hp.ps);// Copy the underlying string
    delete ps;// Free old memory
    ps=new_ p;// Copy data from the right operand to this object
    i=hp. i;
    return *this;// Return this object
    }
  • Behavior image value: the object has its own state, and the copy is completely independent of the original object.
  • Behavior is like a pointer: shared state. When copying an object of this kind, the copy uses the same underlying data as the original object.

Assignment Operators

Exchange operation

  • Classes that manage resources usually also define a function called swap.
  • An algorithm often used to reorder elements.
  • 用swap而不是std::swap。

Object movement

  • After many copy operations, the original object will be destroyed, so the introduction of mobile operation can greatly improve the performance.
  • In the new standard, we can use containers to save types that cannot be copied, as long as they can be moved.
  • Standard library containers, strings, and shared_ PTR class can support both mobile and copy. Class IO and unique_ PTR classes can be moved but not copied.

rvalue reference

  • The new standard introduces right value reference to support move operation.
  • Get the right value reference through & &.
  • Only one object can be bound to be destroyed.
  • A regular reference can be called an lvalue reference.
  • The left value is persistent and the right value is short.

move函数

  • int &&rr2 = std::move(rr1);
  • Move tells the compiler that we have an lvalue, but I want to handle it like an lvalue.
  • Calling move means that we will no longer use rr1 except to assign it or destroy it.

Move constructor and move assignment operator

  • Move constructor:
    The first parameter is a reference of this class type. The key is that this reference parameter is an R-value reference.
    StrVec::StrVec(StrVec &&s) noexcept{}
    Do not allocate any new memory, just take over the given memory.
  • The first parameter is a reference of this class type. The key is that this reference parameter is an R-value reference.
  • StrVec::StrVec(StrVec &&s) noexcept{}
  • Do not allocate any new memory, just take over the given memory.
  • 移动赋值运算符:

    StrVec& StrVec::operator=(StrVec && rhs) noexcept{}

  • StrVec& StrVec::operator=(StrVec && rhs) noexcept{}

< strong > synthetic mobile operation: < / strong >

Only when a class does not define any copy control members of its own version, < strong > and each non static data member of the class can be moved, < / strong >, the compiler will synthesize a move constructor or move assignment operator for it. If there is a move operation in, a composite move operation can be generated.

string

If the displayed requires the compiler to generate a move operation of = default, and the compiler cannot move all members, the compiler will define the move operation as a deleted function.

Whether a class defines its own move operation has an impact on how the copy operation is synthesized. If a class defines a move constructor and / or a move assignment operator, the composite copy constructor and copy assignment operator of the class will be defined as deleted.

  • Move the right value and copy the left value.
  • If there is no move constructor, the right value is also copied.
  • Update the three / five rule: if a class defines any copy operation, it should define all five operations.
  • Move iterator: adapts a given iterator by changing the behavior of its dereference operator
    make_ move_ The iterator function converts an ordinary iterator into a mobile iterator.
  • make_ move_ The iterator function converts an ordinary iterator into a mobile iterator.
  • Recommendation: use mobile operations carefully for performance improvement.
  • When writing a move operation, you must ensure that the original object enters a destructable state in the future

Right value reference and member function

  • Overloaded functions that distinguish between move and copy usually have one version that accepts a const t&, and another version that accepts a t&&.
  • Reference qualifier:
    Prevent a & after the parameter list to restrict that only modifiable left values can be assigned, but not right values.
  • Prevent a & after the parameter list to restrict that only modifiable left values can be assigned, but not right values.
Foo& operator=(const Foo&) &;//防止对右值赋值

The reference qualifier can be & amp; & amp& amp;, For & amp; We can only use it for lvalues; For & amp& amp; Qualified functions can only be used for right values:

Foo &retFoo();//返回一个引用;retFoo调用是一个左值
Foo retVal();//返回一个值,retval调用是一个右值
Foo j;

retFoo()=j; //正确,retfoo返回一个左值
retVal()=j; //错误,retval()返回一个右值
j=retval(); //正确,我们可以将一个右值作为赋值操作的右侧运算对象

A function can be qualified with const and reference at the same time, and the reference qualifier & amp; Must follow const qualifier.

When defining const member functions, you can define two versions. The only difference is that one version has const qualification and the other does not. Reference qualified functions are different. If you define two or more member functions with the same name and the same parameter list, you must add a reference qualifier to all functions or none.