C++ primer 第十三章-類的拷貝控制

Hi!這里是山幺幺的c++ primer系列鸯檬。寫這個(gè)系列的初衷是决侈,雖然在學(xué)校學(xué)習(xí)了c++,但總覺得對(duì)這門語言了解不深喧务,因此我想來啃啃著名的c++ primer赖歌,并在這里同步記錄我的學(xué)習(xí)筆記。由于我啃的是英文版功茴,所以筆記是中英文夾雜的那種庐冯。另外由于已有一定的編程基礎(chǔ),所以這個(gè)系列不會(huì)含有太基礎(chǔ)的知識(shí)坎穿,想入門的朋友最好自己啃書嘻嘻~

前言:Overloaded Operators


性質(zhì)

  • 是命名為'operator' followed by the symbol for the operator being defined的函數(shù)
  • 形參代表the operands of the operator
  • some overloaded operators, assignment among them, must be defined as member functions
  • 若overloaded operator是成員函數(shù)展父,則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
  // ...
};

概述


拷貝控制的內(nèi)容

  • 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

編譯器默認(rèn)拷貝控制

  • 若一個(gè)類沒有定義所有的拷貝控制,編譯器會(huì)自動(dòng)定義 the missing operations

Copy Constructor


性質(zhì)

  • a constructor is the copy constructor if its first parameter is a reference to the class type and any additional parameters have default values
  • 第一個(gè)參數(shù)通常是 const T&玲昧,但也可以是 T&(必須是引用類型栖茉,因?yàn)镮f
    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(編譯器合成的)

  • 若我們沒有為一個(gè)類定義copy constructor,編譯器會(huì)自動(dòng)合成一個(gè)
  • 與synthesized default constructor(只要有constructor<包括copy constructor>就不會(huì)有它了)不同孵延,即使有其他的constructor吕漂,只要沒有copy constructor,編譯器就會(huì)自動(dòng)合成一個(gè) synthesized copy constructor
  • 作用
    • 對(duì)一些類尘应,prevent us from copying objects of that class type惶凝;對(duì)其他類:copies each nonstatic member in turn from the given object into the one being created
  • 不同成員的copy方式
    • 類:使用該類的copy constructor
    • 內(nèi)置類型:直接拷貝
    • 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的區(qū)別
    • 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時(shí)
vector<int> v1(10); // ok: direct initialization
vector<int> v2 = 10; // error: constructor that takes a size is explicit
void f(vector<int>); // f's parameter is copy initialized
f(10); // error: can't use an explicit constructor to copy an argument
f(vector<int>(10)); // ok: directly construct a temporary vector from an int
  • copy initialization發(fā)生的時(shí)機(jī)
    • define variables using an =
    • pass an object as 實(shí)參 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時(shí)會(huì)copy initialize their elements(注意elements created by an emplace member are direct initialized)
  • 遇到copy initialization時(shí)苍鲜,編譯器被允許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混滔,編譯器會(huì)自動(dòng)合成一個(gè)
  • 和copy constructor一樣洒疚,for some classes the synthesized copy-assignment operator disallows assignment;對(duì)其他類: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
  // ...
};

性質(zhì)

  • 每個(gè)類只有一個(gè)destructor遍坟,因?yàn)樗鼪]有形參無法被重載
  • 先執(zhí)行函數(shù)體再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

不同成員的析構(gòu)方式

  • 類:使用該類的destructor(比如智能指針)
  • 內(nèi)置類型: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

析構(gòu)發(fā)生的時(shí)機(jī)

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)
  • 當(dāng)對(duì)象被destroy時(shí),its members are destroyed
  • 當(dāng)容器(包括library container 和 array)被destroy時(shí)愿伴,容器中的元素are destroyed
  • delete時(shí),dynamically allocated objects被destroy
  • temporary objects are destroyed at the end of the full expression in which the temporary was created

Synthesized Destructor

  • 如果我們沒有定義destructor电湘,編譯器會(huì)自動(dòng)合成一個(gè)
  • for some classes, the synthesized destructor is defined to disallow objects of the type from being destroyed隔节;對(duì)其他類:函數(shù)體為空
  • synthesized destructor will not 'delete' a data member that is a pointer

拷貝控制們的關(guān)系


Classes That Need Destructors Need Copy and Assignment

  • 栗子:若使用編譯器合成的copy constructor和copy-assignment operator,會(huì)使得多個(gè)HasPtr objects指向同一塊內(nèi)存寂呛,函數(shù)f返回后怎诫,both hp and ret are destroyed,同一塊內(nèi)存會(huì)被delete兩次贷痪,還會(huì)使得p和q指向無效的內(nèi)存
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


使用場(chǎng)景舉例

  • 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,這相當(dāng)于告訴編譯器我們intentionally not defining these members
  • unlike = default, = delete must appear on the first declaration of a deleted function
  • = delete可以用于任一函數(shù)劫拢,不局限于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:總結(jié)起來,這些規(guī)則意味著:① 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(不然可能會(huì)創(chuàng)造無法destroy的對(duì)象)妹沙;③ if the class has a const or reference member that cannot be default constructed,則default constructor = deleted熟吏;④ if the class has a const(因?yàn)椴荒躠ssign a new value to a const) or reference(因?yàn)锳lthough 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:其他導(dǎo)致copy-control member = deleted的情況會(huì)在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趣斤,所以u(píng)sers 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會(huì)導(dǎo)致不必要的開銷唬渗,相當(dāng)于
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
  • 實(shí)際上我們希望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的錯(cuò)誤使用方法:這種用法會(huì)調(diào)用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典阵,若參數(shù)是HasPtr類型,就會(huì)匹配到swap(HasPtr &lhs, HasPtr &rhs)镊逝;若參數(shù)是內(nèi)置類型或無法匹配到對(duì)應(yīng)的自定義swap壮啊,就會(huì)匹配到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
}
  • 因?yàn)槭莗ass by value,所以形參rhs是一個(gè)臨時(shí) 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執(zhí)行完后,rhs被destroy座菠,destructor deletes the memory to which rhs now points狸眼,即frees the memory to which the left-hand operand had pointed

Move


使用move的時(shí)機(jī)

  • 沒必要用copy(用copy浪費(fèi))時(shí)
  • 不能用copy時(shí):比如IO or unique_ptr classes have a resource (a pointer or an IO buffer) that may not be shared

對(duì)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(因?yàn)橛抑狄媒壎ǖ氖莂n 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);
  • 作用:把左值轉(zhuǎn)換為右值引用類型,告訴編譯器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(因?yàn)閟ometime 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

  • 因?yàn)閙ove操作只是steal resources而并不會(huì)allocate any resources册养,所以它一般并不會(huì)throw any exceptions(但是需要注意:也有可能throw exception的move操作)东帅,所以move constructor和move-assignment operator一般會(huì)有noexcept關(guān)鍵字,用于通知library球拦,避免它do extra work to cater to the possibliity that moving an object of our class type might throw
  • vector的push_back默認(rèn)使用copy constructor而不是move constructor靠闭,因?yàn)椋?
    • 基礎(chǔ)知識(shí):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 拋出時(shí)原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 標(biāo)上 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

  • 編譯器不一定會(huì)生成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的:內(nèi)置類型蜗帜、有相應(yīng)move operation的類及其成員);除非explicitly ask the compiler to generate a move operation by using = default
    特殊情況:當(dāng)基類的析構(gòu)函數(shù)是virtual時(shí)资厉,若子類定義了析構(gòu)函數(shù)—even if
    it uses = default to use the synthesized version—the compiler will not synthesize a move operation for 子類
// 假設(shè)Y是沒有move constructor的類
struct hasY {
  hasY() = default;
  hasY(hasY&&) = default; // 若沒有這句話厅缺,編譯器不會(huì)synthesize move constructor
  Y mem; // hasY will have a synthesized deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
  • 編譯器合成的移動(dòng)構(gòu)造函數(shù)會(huì)復(fù)制內(nèi)置類型成員并調(diào)用其他成員的移動(dòng)構(gòu)造函數(shù)
    • 對(duì)內(nèi)置類型的成員:和復(fù)制構(gòu)造一樣只是拷貝值
    • 對(duì)其他成員(比如string):移動(dòng)構(gòu)造函數(shù)會(huì)先拷貝值,再使被拷貝的對(duì)象失效
class A {
public:
    int *p = new int;
    string str = "Fuck";
};

int main() {
    A a;
    A b(std::move(a)); // move可以理解為強(qiáng)制類型轉(zhuǎn)換酌住,把左值轉(zhuǎn)為右值店归,轉(zhuǎn)為右值后,就會(huì)匹配到移動(dòng)構(gòu)造函數(shù)
        cout << a.p << " " << b.p << endl; // a.p和b.p地址相同酪我,a.p沒有失效(即沒有變?yōu)閚ullptr)
    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也是調(diào)用的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可以共用一個(gè)assignment operator:因?yàn)閰?shù)不是引用且叁,所以需要用賦值語句右邊的對(duì)象(記為x)初始化rhs都哭。若x為左值,則使用復(fù)制構(gòu)造函數(shù)構(gòu)造rhs逞带,此時(shí)相當(dāng)于是copy assignment欺矫,結(jié)果會(huì)copy and swap,x不受影響展氓;若x為右值穆趴,則使用移動(dòng)構(gòu)造函數(shù)構(gòu)造rhs,此時(shí)相當(dāng)于是move assignment遇汞,結(jié)果會(huì)copy and swap未妹,x被move
class HasPtr {
public:
  // 構(gòu)造函數(shù)
  HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }
  // 復(fù)制構(gòu)造函數(shù)
  HasPtr(const HasPtr &p): ps(new std::string(*p.ps)), i(p.i) { }
  // 移動(dòng)構(gòu)造函數(shù)
  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; }
  // 析構(gòu)函數(shù)
  ~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時(shí),會(huì)調(diào)用copy

成員函數(shù)的兩態(tài)


  • 其一參數(shù)為常左值引用空入,另一為非常右值引用
  • 栗子: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


  • &或&&分別表示該函數(shù)中的this只能指向左值或右值:以下代碼表示Foo類型的右值不能出現(xiàn)在賦值語句左邊
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的函數(shù)络它,&或&&寫在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<int> 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 &
  • 只要有一個(gè)重載函數(shù)有reference qualifier,那么其他和它重載函數(shù)也必須有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ǔ)充:深拷貝與淺拷貝


  • 假設(shè)B復(fù)制了A歪赢,修改A的時(shí)候化戳,看B是否發(fā)生變化:
    • 如果B跟著也變了,說明是淺拷貝(指向被復(fù)制的內(nèi)存地址)
    • 如果B沒有改變埋凯,說明是深拷貝(開辟一塊新的內(nèi)存地址用于存放復(fù)制的對(duì)象)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末点楼,一起剝皮案震驚了整個(gè)濱河市扫尖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掠廓,老刑警劉巖换怖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異却盘,居然都是意外死亡狰域,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門黄橘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兆览,“玉大人,你說我怎么就攤上這事塞关√剑” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵帆赢,是天一觀的道長(zhǎng)小压。 經(jīng)常有香客問我,道長(zhǎng)椰于,這世上最難降的妖魔是什么怠益? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮瘾婿,結(jié)果婚禮上蜻牢,老公的妹妹穿的比我還像新娘。我一直安慰自己偏陪,他們只是感情好抢呆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著笛谦,像睡著了一般抱虐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饥脑,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天恳邀,我揣著相機(jī)與錄音,去河邊找鬼好啰。 笑死轩娶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的框往。 我是一名探鬼主播鳄抒,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了许溅?” 一聲冷哼從身側(cè)響起瓤鼻,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贤重,沒想到半個(gè)月后茬祷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡并蝗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年祭犯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滚停。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沃粗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出键畴,到底是詐尸還是另有隱情最盅,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布起惕,位于F島的核電站涡贱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惹想。R本人自食惡果不足惜问词,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘀粱。 院中可真熱鬧戏售,春花似錦、人聲如沸草穆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悲柱。三九已至,卻和暖如春些己,著一層夾襖步出監(jiān)牢的瞬間豌鸡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工段标, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涯冠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓逼庞,卻偏偏與公主長(zhǎng)得像蛇更,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355