C++ primer 第十三章-类的拷贝控制

Hi!这里是山幺幺的c++ primer系列。写这个系列的初衷是,虽然在学校学习了c++,但总觉得对这门语言了解不深,因此我想来啃啃著名的c++ primer,并在这里同步记录我的学习笔记。由于我啃的是英文版,所以笔记是中英文夹杂的那种。另外由于已有一定的编程基础,所以这个系列不会含有太基础的知识,想入门的朋友最好自己啃书嘻嘻~

前言:Overloaded Operators


性质

  • 是命名为'operator' followed by the symbol for the operator being defined的函数
  • 形参代表the operands of the operator
  • some overloaded operators, assignment among them, must be defined as member functions
  • 若overloaded operator是成员函数,则the left-hand operand is bound to 'this'

overloaded assignment operators

  • overloaded assignment operators usually return a reference to their left-hand operand
  • the library generally requires that types stored in a container have assignment operators that return a reference to the left-hand operand
  • 栗子
class Foo {
public:
  Foo& operator=(const Foo&); // assignment operator
  // ...
};

概述


拷贝控制的内容

  • copy constructormove constructor:define what happens when an object is initialized from another object of the same type
  • copy-assignment operatormove-assignment operator:define what happens when we assign an object of a class type to another object of that same class type
  • destructor:defines what happens when an object of the type ceases to exist

编译器默认拷贝控制

  • 若一个类没有定义所有的拷贝控制,编译器会自动定义 the missing operations

Copy Constructor


性质

  • a constructor is the copy constructor if its first parameter is a reference to the class type and any additional parameters have default values
  • 第一个参数通常是 const T&,但也可以是 T&(必须是引用类型,因为If
    that parameter were not a reference, then the call would never succeed—to call the copy constructor, we’d need to use the copy constructor to copy the argument, but to copy the argument, we’d need to call the copy constructor, and so on indefinitely.)
  • copy constructor 通常不是 explicit 的

Synthesized Copy Constructor(编译器合成的)

  • 若我们没有为一个类定义copy constructor,编译器会自动合成一个
  • 与synthesized default constructor(只要有constructor<包括copy constructor>就不会有它了)不同,即使有其他的constructor,只要没有copy constructor,编译器就会自动合成一个 synthesized copy constructor
  • 作用
    • 对一些类,prevent us from copying objects of that class type;对其他类:copies each nonstatic member in turn from the given object into the one being created
  • 不同成员的copy方式
    • 类:使用该类的copy constructor
    • 内置类型:直接拷贝
    • array:array不能被拷贝,所以是拷贝each element
// equivalent to the copy constructor that would be synthesized for Sales_data 
Sales_data::Sales_data(const Sales_data &orig):
bookNo(orig.bookNo), // uses the string copy constructor
units_sold(orig.units_sold), // copies orig.units_sold
revenue(orig.revenue) // copies orig.revenue
{ } // empty body

Copy Initialization

  • 与direct initialization的区别
    • direct initialization:编译器使用function matching找到最匹配的constructor
    • copy initialization:编译器使用copy constructor 或 move constructor 来 copy the right -hand operand into the object being created
string dots(10, '.'); // direct initialization
string s(dots); // direct initialization
string s2 = dots; // copy initialization
string null_book = "9-999-99999-9"; // copy initialization
string nines = string(100, '9'); // copy initialization
  • 不能使用copy initialization的情形:require conversion by an explicit constructor时
vector v1(10); // ok: direct initialization
vector v2 = 10; // error: constructor that takes a size is explicit
void f(vector); // f's parameter is copy initialized
f(10); // error: can't use an explicit constructor to copy an argument
f(vector(10)); // ok: directly construct a temporary vector from an int
  • copy initialization发生的时机
    • define variables using an =
    • pass an object as 实参 to 非形参 of nonreference type
    • return an object from a function that has a nonreference return type
    • brace initialize the elements in an array or the members of an aggregate class
    • library containers在容器初始化或使用push_back、insert时会copy initialize their elements(注意elements created by an emplace member are direct initialized)
  • 遇到copy initialization时,编译器被允许skip the copy/move constructor and create the object directly,即把
    string null_book = "9-999-99999-9"; // copy initialization
    
    改写为
    string null_book("9-999-99999-9"); // compiler omits the copy constructor
    
    PS:即使编译器这么做了,copy/move constructor must exist and must be accessible (e.g., not private)

Copy-Assignment Operator


栗子

Sales_data trans, accum;
trans = accum; // uses the Sales_data copy-assignment operator

写法注意

  • Assignment operators must work correctly if an object is assigned to itself

Synthesized Copy-Assignment Operator

  • 如果我们没有定义copy-assignment operator,编译器会自动合成一个
  • 和copy constructor一样,for some classes the synthesized copy-assignment operator disallows assignment;对其他类:assigns each nonstatic member of the right-hand object to the corresponding member of the left-hand object using the copy-assignment operator for the type of that member
  • returns a reference to its left-hand object
  • array:assigned by assigning each element of the array
// equivalent to the synthesized copy-assignment operator
Sales_data& Sales_data::operator=(const Sales_data &rhs) {
  bookNo = rhs.bookNo; // calls the string::operator=
  units_sold = rhs.units_sold; // uses the built-in int assignment
  revenue = rhs.revenue; // uses the built-in double assignment
  return *this; // return a reference to this object
}

Destructor


作用

  • do whatever work is needed to free the resources used by an object
  • destroy the nonstatic data members of the object

栗子

class Foo {
public:
  ~Foo(); // destructor
  // ...
};

性质

  • 每个类只有一个destructor,因为它没有形参无法被重载
  • 先执行函数体再destroy members
  • members are destroyed in reverse order from the order in which they were initialized
  • the destructor body does not directly destroy the members themselves;members are destroyed as part of the implicit destruction phase that follows the destructor body

不同成员的析构方式

  • 类:使用该类的destructor(比如智能指针)
  • 内置类型:built-in types do not have destructors, so nothing is done to destroy members of built-in type
    PS:The implicit destruction of a member of built-in pointer type does not delete the object to which that pointer points

析构发生的时机

destructor is used automatically whenever an object of its type is destroyed

  • variables are destroyed when they go out of scope(destructor is not run when a reference or a pointer to an object goes out of scope)
  • 当对象被destroy时,its members are destroyed
  • 当容器(包括library container 和 array)被destroy时,容器中的元素are destroyed
  • delete时,dynamically allocated objects被destroy
  • temporary objects are destroyed at the end of the full expression in which the temporary was created

Synthesized Destructor

  • 如果我们没有定义destructor,编译器会自动合成一个
  • for some classes, the synthesized destructor is defined to disallow objects of the type from being destroyed;对其他类:函数体为空
  • synthesized destructor will not 'delete' a data member that is a pointer

拷贝控制们的关系


Classes That Need Destructors Need Copy and Assignment

  • 栗子:若使用编译器合成的copy constructor和copy-assignment operator,会使得多个HasPtr objects指向同一块内存,函数f返回后,both hp and ret are destroyed,同一块内存会被delete两次,还会使得p和q指向无效的内存
class HasPtr {
public:
  HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }
  ~HasPtr() { delete ps; }
  // ...
};

HasPtr f(HasPtr hp) // HasPtr passed by value, so it is copied
{
  HasPtr ret = hp; // copies the given HasPtr
  // process ret
  return ret; // ret and hp are destroyed
}

HasPtr p("some values");
f(p); // when f completes, the memory to which p.ps points is freed
HasPtr q(p); // now both p and q point to invalid memory!
  • 例外:If a base class has an empty destructor in order to make it virtual, then the fact that the class has a destructor does not indicate that the assignment operator or copy constructor is also needed

Classes That Need Copy Need Assignment, and Vice Versa

  • 但不一定需要destructor

= default


作用

  • ask the compiler to generate the synthesized versions of the copy-control members

栗子

class Sales_data {
public:
  // copy control; use defaults
  Sales_data() = default;
  Sales_data(const Sales_data&) = default;
  Sales_data& operator=(const Sales_data &);
  ~Sales_data() = default;
  // other members as before
};
Sales_data& Sales_data::operator=(const Sales_data&) = default;
  • = default在类中的是inline的,在类外的不是
  • We can use = default only on member functions that have a synthesized
    version (i.e., the default constructor or a copy-control member)

Preventing Copies


使用场景举例

  • iostream classes prevent copying to avoid letting multiple objects write to or read from the same IO buffer

= delete

  • 是c++ 11的新特性
  • 作用:we can prevent copies by defining the copy constructor and copy-assignment operator as deleted functions,这相当于告诉编译器我们intentionally not defining these members
  • unlike = default, = delete must appear on the first declaration of a deleted function
  • = delete可以用于任一函数,不局限于copy-control member,除了:
    1. 我们不能define variables or create temporaries of a type that has a deleted destructor
    2. 我们不能define variables or temporaries of a class that has a member whose type has a deleted destructor
    struct NoDtor {
      NoDtor() = default; // use the synthesized default constructor
      ~NoDtor() = delete; // we can't destroy objects of type NoDtor
     };
    NoDtor nd; // error: NoDtor destructor is deleted
    NoDtor *p = new NoDtor(); // ok: but we can't delete p
    delete p; // error: NoDtor destructor is deleted
    
  • 栗子
struct NoCopy {
  NoCopy() = default; // use the synthesized default constructor
  NoCopy(const NoCopy&) = delete; // no copy
  NoCopy &operator=(const NoCopy&) = delete; // no assignment
  ~NoCopy() = default; // use the synthesized destructor
  // other members
};
  • 这些编译器合成的copy control member是deleted:
    1. the synthesized destructor is defined as deleted if the class has a member whose own destructor is deleted or is inaccessible (e.g., private)
    2. the synthesized copy constructor is defined as deleted if the class has a member whose own copy constructor / destructor is deleted / inaccessible
    3. the synthesized copy-assignment operator is defined as deleted if a member has a deleted or inaccessible copy-assignment operator, or if the class has a const or reference member
    4. the synthesized default constructor is defined as deleted if the class has a member with a deleted or inaccessible destructor; or has a reference member that does not have an in-class initializer; or has a const member whose type does not explicitly define a default constructor and that member does not have an in-class initializer
    5. the synthesized copy constructor and copy-assignment will be defined as deleted if the class defines either a move constructor and/or a move-assignment operator

PS:总结起来,这些规则意味着:① if a class has a data member that cannot be default constructed, copied, assigned, or destroyed, then the corresponding member will be a deleted function;② a member that has a deleted or inaccessible destructor causes the synthesized default and copy constructors to be defined as deleted(不然可能会创造无法destroy的对象);③ if the class has a const or reference member that cannot be default constructed,则default constructor = deleted;④ if the class has a const(因为不能assign a new value to a const) or reference(因为Although we can assign a new value to a reference, doing so changes the value of the object to which the reference refers) member,则copy-assignment operator = deleted

再PS:其他导致copy-control member = deleted的情况会在15、19章讲到

private

  • prevent copy的方法:定义copy constructor and copy-assignment operator为private,但这样friends and members of the class can still make copies(若只声明为private而不定义,则可以阻止friends and members of the class的copy行为,attempt to use an undefined member results in a link-time failure,所以code that tries to make a copy will be flagged as an error at compile time)
  • 栗子
class PrivateCopy {
  // copy control is private and so is inaccessible to ordinary user code
  PrivateCopy(const PrivateCopy&);
  PrivateCopy &operator=(const PrivateCopy&);
  // other members
public:
  PrivateCopy() = default; // use the synthesized default constructor
  ~PrivateCopy(); // destructor是public,所以users can define objects of this type( but not copy them )
};

Valuelike vs Pointerlike Copy


定义

  • valuelike:the copy and the original are independent of each other(比如library container 和 string)
  • pointerlike:copy and the original use the same underlying data;changes made to the copy also change the original, and vice versa(比如shared_ptr)
    PS:IO types and unique_ptr不允许拷贝和赋值,所以两种都没有

栗子

  • valuelike behavior
class HasPtr {
public:
  HasPtr(const std::string &s = std::string()):
  ps(new std::string(s)), i(0) { }
  // each HasPtr has its own copy of the string to which ps points
  HasPtr(const HasPtr &p): ps(new std::string(*p.ps)), i(p.i) { }
  HasPtr& operator=(const HasPtr &);
  ~HasPtr() { delete ps; }
private:
  std::string *ps;
  int i;
};

HasPtr& HasPtr::operator=(const HasPtr &rhs) {
  auto newp = new string(*rhs.ps); // copy the underlying string
  delete ps; // free the old memory
  ps = newp; // copy data from rhs into this object
  i = rhs.i;
  return *this; // return this object
}
  • pointerlike behavior
class HasPtr {
public:
  // constructor allocates a new string and a new counter, which it sets to 1
  HasPtr(const std::string &s = std::string()):
  ps(new std::string(s)), i(0), use(new std::size_t(1)) {}
  // copy constructor copies all three data members and increments the counter
  HasPtr(const HasPtr &p):
  ps(p.ps), i(p.i), use(p.use) { ++*use; }
  HasPtr& operator=(const HasPtr&);
  ~HasPtr();
private:
  std::string *ps;
  int i;
  std::size_t *use; // member to keep track of how many objects share *ps
};

HasPtr::~HasPtr() {
  if (--*use == 0) { // if the reference count goes to 0
    delete ps; // delete the string
    delete use; // and the counter
  }
}

HasPtr& HasPtr::operator=(const HasPtr &rhs) {
  ++*rhs.use; // increment the use count of the right-hand operand
  if (--*use == 0) { // then decrement this object's counter
    delete ps; // if no other users
    delete use; // free this object's allocated members
  }
  ps = rhs.ps; // copy data from rhs into this object
  i = rhs.i;
  use = rhs.use;
  return *this; // return this object
}

Swap


the library swap

  • std中的swap会导致不必要的开销,相当于
HasPtr temp = v1; // make a temporary copy of the value of v1
v1 = v2; // assign the value of v2 to v1
v2 = temp; // assign the saved value of v1 to v2
  • 实际上我们希望swap是
string *temp = v1.ps; // make a temporary copy of the pointer in v1.ps
v1.ps = v2.ps; // assign the pointer in v2.ps to v1.ps
v2.ps = temp; // assign the saved pointer in v1.ps to v2.ps

自定义swap

  • 栗子
class HasPtr {
  friend void swap(HasPtr&, HasPtr&);
  // other members
};
inline void swap(HasPtr &lhs, HasPtr &rhs) {
  using std::swap;
  swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
  swap(lhs.i, rhs.i); // swap the int members
}
  • 自定义swap的错误使用方法:这种用法会调用library swap
void swap(Foo &lhs, Foo &rhs) {
  // WRONG: this function uses the library version of swap, not the HasPtr version
  std::swap(lhs.h, rhs.h);
  // swap other members of type Foo
}
  • 自定义swap的正确使用方法:这段代码第三行的swap,若参数是HasPtr类型,就会匹配到swap(HasPtr &lhs, HasPtr &rhs);若参数是内置类型或无法匹配到对应的自定义swap,就会匹配到library swap
void swap(Foo &lhs, Foo &rhs) {
  using std::swap;
  swap(lhs.h, rhs.h); // uses the HasPtr version of swap
  // swap other members of type Foo
}

copy and swap

// note rhs is passed by value, which means the HasPtr copy constructor
// copies the string in the right-hand operand into rhs
HasPtr& HasPtr::operator=(HasPtr rhs) {
  // swap the contents of the left-hand operand with the local variable rhs
  swap(*this, rhs); // rhs now points to the memory this object had used
  return *this; // rhs is destroyed, which deletes the pointer in rhs
}
  • 因为是pass by value,所以形参rhs是一个临时 copy of the right-hand
    operand;swap puts the pointer that had been in the left-hand operand into rhs, and puts the pointer that was in rhs into *this;assignment operator执行完后,rhs被destroy,destructor deletes the memory to which rhs now points,即frees the memory to which the left-hand operand had pointed

Move


使用move的时机

  • 没必要用copy(用copy浪费)时
  • 不能用copy时:比如IO or unique_ptr classes have a resource (a pointer or an IO buffer) that may not be shared

对move和copy的支持

  • library containers, string, and shared_ptr:支持move和copy
  • IO and unique_ptr:支持move不支持copy

右值引用

  • c++ 11 新特性
  • 定义:a reference that must be bound to an rvalue
  • we are free to “move” resources from an rvalue reference to another object(因为右值引用绑定的是an object that is about to be destroyed)
  • 非常量左(右)值引用只能引用非常量左(右)值;常量左值引用可以引用任何值;常量右值引用只能引用常量右值和非常量右值
  • 常见返回左值的:functions that return lvalue references、assignment、subscript、dereference、prefix increment/decrement
  • 常见返回右值的:functions that return a nonreference type、arithmetic/relational/bitwise/postfix increment/postfix decrement operators、literal
  • 注意:右值引用类型的变量也是左值!
    int &&rr1 = 42; // ok: literals are rvalues
    int &&rr2 = rr1; // error: the expression rr1 is an lvalue!
    

library move

  • 定义在头文件utility中
  • 栗子
int &&rr3 = std::move(rr1);
  • 作用:把左值转换为右值引用类型,告诉编译器we have an lvalue that we want to treat as if it were an rvalue
  • 在move之后,除非重新赋值/destroy,被move的变量其值是不确定的

move constructor

  • 形参是右值引用类型,any additional parameters must all have default arguments
  • 注意:move constructor需保证the original object must no longer point to those moved resources,从而可以被安全地destroy
  • 栗子
class StrVec {
public:
  StrVec(StrVec&&) noexcept; // move constructor
  // other members as before
};

StrVec::StrVec(StrVec &&s) noexcept // move won't throw any exceptions
  // member initializers take over the resources in s
  : elements(s.elements), first_free(s.first_free), cap(s.cap) {
  // leave s in a state in which it is safe to run the destructor
  s.elements = s.first_free = s.cap = nullptr;
}

move assignment

  • move-assignment operator must guard against self-assignment
  • 栗子
StrVec &StrVec::operator=(StrVec &&rhs) noexcept {
  // direct test for self-assignment
  if (this != &rhs) {
  free(); // free existing elements
  elements = rhs.elements; // take over resources from rhs
  first_free = rhs.first_free;
  cap = rhs.cap;
  // leave rhs in a destructible state
  rhs.elements = rhs.first_free = rhs.cap = nullptr;
  }
  return *this;
}

move operation(包括move assignment和constructor)需保证的

  • leave the moved-from object in a state that is safe to destroy(因为sometime after the move operation completes, the moved-from object will be destroyed)
  • guarantee that the moved-from object remains valid(即can safely be given a new value or used in other ways that do not depend on its current value),从而we can run operations such as as empty or size on moved-from strings,但 we don’t know what result we’ll get. We might expect a moved-from object to be empty, but that is not guaranteed

noexcept

  • 因为move操作只是steal resources而并不会allocate any resources,所以它一般并不会throw any exceptions(但是需要注意:也有可能throw exception的move操作),所以move constructor和move-assignment operator一般会有noexcept关键字,用于通知library,避免它do extra work to cater to the possibliity that moving an object of our class type might throw
  • vector的push_back默认使用copy constructor而不是move constructor,因为:
    • 基础知识:push_back on a vector might require that the vector be reallocated;vector 需保证 if an exception happens when we call push_back, the vector itself will be left unchanged
    • 若使用move constructor:如果move constructor throws an exception after moving some but not all of the elements, the moved-from elements in the old space would have been changed, and the unconstructed elements in the new space would not yet exist,这样的话 vector 就无法保证它 left unchanged了
    • 若使用copy constructor:while the elements are being constructed in the new memory, the old elements remain unchanged,所以可以保证 exception 抛出时原vector remains unchanged
    • 因此,vector must use a copy constructor instead of a move constructor during reallocation unless it knows that the element type’s move constructor cannot throw an exception,所以如果想让它用move而不是copy,需要给move constructor and move-assignment operator 标上 noexcept

= delete

  • move operation is never implicitly defined as deleted
  • 如果我们 explicitly ask the compiler to generate a move operation by using = default,且编译器无法move所有成员,那么move operation will be defined as deleted
  • 在以下情形,move constructor is defined as deleted:
    • 类中有成员定义了自己的copy constructor,但未定义自己的move constructor
    • 类中有成员未定义自己的copy operations,且编译器无法synthesize a move constructor
    • 类中有成员,其move constructor是deleted或inaccessible
    • 类的destructor is deleted or inaccessible
  • 在以下情形,move-assignment operator is defined as deleted:
    • 第一、二、三条与 move constructor 的第一、二、三条类似,只是move constructor换成move-assignment operator
    • 类中有const or reference member

Synthesized Move Operations

  • 编译器不一定会生成move constructor / assignment:compiler will synthesize a move constructor or a move-assignment operator only if the class doesn’t define any of its own copy-control members and if every nonstatic data member of the class can be moved(can be moved的:内置类型、有相应move operation的类及其成员);除非explicitly ask the compiler to generate a move operation by using = default
    特殊情况:当基类的析构函数是virtual时,若子类定义了析构函数—even if
    it uses = default to use the synthesized version—the compiler will not synthesize a move operation for 子类
// 假设Y是没有move constructor的类
struct hasY {
  hasY() = default;
  hasY(hasY&&) = default; // 若没有这句话,编译器不会synthesize move constructor
  Y mem; // hasY will have a synthesized deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
  • 编译器合成的移动构造函数会复制内置类型成员并调用其他成员的移动构造函数
    • 对内置类型的成员:和复制构造一样只是拷贝值
    • 对其他成员(比如string):移动构造函数会先拷贝值,再使被拷贝的对象失效
class A {
public:
    int *p = new int;
    string str = "Fuck";
};

int main() {
    A a;
    A b(std::move(a)); // move可以理解为强制类型转换,把左值转为右值,转为右值后,就会匹配到移动构造函数
        cout << a.p << " " << b.p << endl; // a.p和b.p地址相同,a.p没有失效(即没有变为nullptr)
    cout << a.str; // a.str为空
        cout << b.str; // b.str为Fuck
}

使用move operation需注意的

  • a moved-from object has indeterminate state
  • 定义了move constructor or move-assignment operator的类必须定义their own copy operations(否则those members are deleted by default)
  • 有copy constructor(assignment)但没有move constructor(assignment)的类,即使使用move也是调用的copy constructor(assignment)
class Foo {
public:
  Foo() = default;
  Foo(const Foo&); // copy constructor
  // other members, but Foo does not define a move constructor
};
Foo x;
Foo y(x); // copy constructor; x is an lvalue
Foo z(std::move(x)); // copy constructor, because there is no move constructor
  • move和copy可以共用一个assignment operator:因为参数不是引用,所以需要用赋值语句右边的对象(记为x)初始化rhs。若x为左值,则使用复制构造函数构造rhs,此时相当于是copy assignment,结果会copy and swap,x不受影响;若x为右值,则使用移动构造函数构造rhs,此时相当于是move assignment,结果会copy and swap,x被move
class HasPtr {
public:
  // 构造函数
  HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }
  // 复制构造函数
  HasPtr(const HasPtr &p): ps(new std::string(*p.ps)), i(p.i) { }
  // 移动构造函数
  HasPtr(HasPtr &&p) noexcept : ps(p.ps), i(p.i) {p.ps = 0;}
  // assignment operator is both the move- and copy-assignment operator
  HasPtr& operator=(HasPtr rhs) { swap(*this, rhs); return *this; }
  // 析构函数
  ~HasPtr() { delete ps; }
private:
  std::string *ps;
  int i;
};

hp = hp2; // hp2 is an lvalue; copy constructor used to copy hp2
hp = std::move(hp2); // move constructor moves hp2
  • we usually do not provide a using declaration for move. When we use move, we call std::move, not move
  • 没有move时,会调用copy

成员函数的两态


  • 其一参数为常左值引用,另一为非常右值引用
  • 栗子:push_back
void push_back(const X&); // copy: binds to any kind of X
void push_back(X&&); // move: binds only to modifiable rvalues of type X; free to steal resources from its parameter

StrVec vec; // empty StrVec
string s = "some string or another";
vec.push_back(s); // calls push_back(const string&)
vec.push_back("done"); // calls push_back(string&&)

Reference Qualifier


  • &或&&分别表示该函数中的this只能指向左值或右值:以下代码表示Foo类型的右值不能出现在赋值语句左边
class Foo {
public:
  Foo &operator=(const Foo&) &; // may assign only to modifiable lvalues
  // other members of Foo
};

Foo &Foo::operator=(const Foo &rhs) & {
  // do whatever is needed to assign rhs to this object
  return *this;
}
  • reference qualifier may appear only on a (nonstatic) member function
  • reference qualifier must appear in both the declaration and definition of the function
  • both const and reference qualified的函数,&或&&写在const后面
class Foo {
public:
  Foo someMem() & const; // error: const qualifier must come first
  Foo anotherMem() const &; // ok: const qualifier comes first
};
  • 我们可以overload a function based on its reference qualifier
class Foo {
public:
  Foo sorted() &&; // may run on modifiable rvalues
  Foo sorted() const &; // may run on any kind of Foo
  // other members of Foo
private:
  vector data;
};
// this object is an rvalue, so we can sort in place
Foo Foo::sorted() && {
  sort(data.begin(), data.end());
  return *this;
}
// this object is either const or it is an lvalue; either way we can't sort in place
Foo Foo::sorted() const & {
  Foo ret(*this); // make a copy
  sort(ret.data.begin(), ret.data.end()); // sort the copy
  return ret; // return the copy
}

retVal().sorted(); // retVal() is an rvalue, calls Foo::sorted() &&
retFoo().sorted(); // retFoo() is an lvalue, calls Foo::sorted() const &
  • 只要有一个重载函数有reference qualifier,那么其他和它重载函数也必须有reference qualifier
class Foo {
public:
  Foo sorted() &&;
  Foo sorted() const; // error: must have reference qualifier
  using Comp = bool(const int&, const int&);
  Foo sorted(Comp*); // ok: different parameter list
  Foo sorted(Comp*) const; // ok: neither version is reference qualified
};

补充:深拷贝与浅拷贝


  • 假设B复制了A,修改A的时候,看B是否发生变化:
    • 如果B跟着也变了,说明是浅拷贝(指向被复制的内存地址)
    • 如果B没有改变,说明是深拷贝(开辟一块新的内存地址用于存放复制的对象)

你可能感兴趣的:(C++ primer 第十三章-类的拷贝控制)