[c++primer][13]複製控制

複製構造函數、賦值操做符和析構函數總稱爲複製控制。app

複製構造函數:特殊的構造函數,具備單個形參,該形參時對該類類型的const引用。定義新對象並用同類型對象初始化,顯式調用了複製構造函數;將該類型對象傳遞給函數或從函數返回該類型的對象時,隱式調用了複製構造函數。函數

析構函數:當對象超出做用域或動態分配的對象被刪除時,自動調用析構函數,用於釋放在造函數或在對象生命期內獲取的資源。fetch

13.1 複製構造函數

對類類型對象來講,this

1)直接初始化直接調用與實參匹配的構造函數指針

2)複製初始化老是調用複製構造函數(複製初始化首先使用指定構造函數建立一個臨時對象,而後用複製構造函數將臨時對象複製到正在建立的對象)code

合成的複製構造函數

若是咱們沒有定義複製構造,編譯器會爲咱們合成。即便定義了其餘構造函數,也會合成。對象

合成複製構造函數的行爲是,執行逐個成員初始化,將新對象初始化爲原對象的副本。blog

定義本身的複製構造函數

只包含類類型成員和內置類型(不是指針)成員的類,無需顯式定義複製構造函數;ip

複製構造函數的定義也可使用初始化列表,能夠在函數體中作任何其餘必要工做。內存

禁止複製

爲防止複製,類必須顯式聲明其複製構造函數爲private;若是想要連友元和成員中的複製也禁止,就能夠聲明爲private而不定義。

13.2 賦值操做符

合成賦值操做符

合成賦值操做符執行逐個成員賦值,返回*this,是對左操做數對象的引用。

可使用合成複製構造函數的類一般也可使用合成賦值操做符

13.3 析構函數

什麼時候調用析構函數

撤銷類對象(超出做用域)會自動調用析構函數。

當對象的引用或指針超出做用域時,不會運行析構函數。只有刪除指向動態分配對象的指針或實際對象(而不是引用)超出做用域時,纔會運行析構函數。

三法則:若是須要析構函數,則須要全部三個複製控制成員。 

合成析構函數

與複製構造和賦值操做符不一樣,編譯器老是(即使本身編寫了析構)會爲咱們合成一個析構函數。合成析構函數按成員在類中聲明次序的逆序撤銷成員。對於類類型成員,合成析構函數調用該成員的析構函數來撤銷該對象。

執行順序:先執行類定義的析構,再運行合成析構函數

13.5 管理指針成員

管理指針成員的三種方法

1)指針成員採起常規指針型行爲。具備指針的全部缺陷但無需特殊的複製控制

使用默認合成複製構造函數

沒法避免懸垂指針(指針指向的內存被釋放,指針指向一個不復存在的對象)

2)使用智能指針。指針所指向的對象是共享的,但類可以防止懸垂指針

3)類採起值型行爲。指針所指向的對象是惟一的。由每一個類對象獨立管理。

定義智能指針類

使用計數類

// private class for use by HasPtr only
class U_Ptr {
  friend class HasPtr;
  int *ip;
  size_t use;
  U_Ptr(int *p): ip(p), use(1) { }
  ~U_Ptr() { delete ip; }
};

使用計數類的使用

/* smart pointer class: takes ownership of the dynamically allocated
* object to which it is bound
* User code must dynamically allocate an object to initialize a HasPtr
* and must not delete that object; the HasPtr class will delete it
*/
class HasPtr {
public:
  // HasPtr owns the pointer; pmust have been dynamically allocated
  HasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i) { }
  // copy members and increment the use count
  HasPtr(const HasPtr &orig):
  ptr(orig.ptr), val(orig.val) { ++ptr->use; }
  HasPtr& operator=(const HasPtr&);
  // if use count goes to zero, delete the U_Ptr object
  ~HasPtr() { if (--ptr->use == 0) delete ptr; }
private:
  U_Ptr *ptr; // points to use-counted U_Ptr class
  int val;
};

賦值與使用計數

HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
  ++rhs.ptr->use; // increment use count on rhs first
  if (--ptr->use == 0)
  delete ptr; // if use count goes to 0 on this object, delete it
  ptr = rhs.ptr; // copy the U_Ptr object
  val = rhs.val; // copy the int member
  return *this;
}

定義值型類

給指針成員提供值語義,複製值型對象時,會獲得一個不一樣的新副本;要使指針成員表現得像一個值,賦值對象時必須複製指針所指向的對象。

/*
* Valuelike behavior even though HasPtr has a pointer member:
* Each time we copy a HasPtr object, we make a new copy of the
* underlying int object to which ptr points.
*/
class HasPtr {
public:
  // no point to passing a pointer if we're going to copy it anyway
  // store pointer to a copy of the object we're given
  HasPtr(const int &p, int i): ptr(new int(p)), val(i) {}
  // copy members and increment the use count
  HasPtr(const HasPtr &orig):
  ptr(new int (*orig.ptr)), val(orig.val) { }
  HasPtr& operator=(const HasPtr&);
  ~HasPtr() { delete ptr; }
  // accessors must change to fetch value from Ptr object
  int get_ptr_val() const { return *ptr; }
  int get_int() const { return val; }
  // change the appropriate data member
  void set_ptr(int *p) { ptr = p; }
  void set_int(int i) { val = i; }
  // return or change the value pointed to, so ok for const objects
  int *get_ptr() const { return ptr; }
  void set_ptr_val(int p) const { *ptr = p; }
private:
  int *ptr; // points to an int
  int val;
};

賦值操做符不須要分配新對象,它只是必須記得給其指針所指向的對象賦新值,而不是給指針自己賦值。(即便要將一個對象賦值給它自己,賦值操做符也必須老是保證正確。)

HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
  // Note: Every HasPtr is guaranteed to point at an actual int;
  // We know that ptr cannot be a zero pointer
  *ptr = *rhs.ptr; // copy the value pointed to
  val = rhs.val; // copy the int
  return *this;
}
相關文章
相關標籤/搜索