C++拷貝控制(copy control)

拷貝、賦值、銷燬

構造函數 ⇒ 對象初始化行爲
賦值運算符 ⇒ 對象間賦值行爲
析構函數 ⇒ 銷燬行爲

拷貝構造函數

copy constructor:第一個參數爲自身(幾乎都爲const)的引用,額外參數都有默認值的構造函數
拷貝初始化
直接初始化=函數匹配
拷貝初始化=運算 (copy initialization) ⇒ 拷貝構造函數 or 移動構造函數
1."="定義變量
2.實參傳遞非引用形參
3.返回非引用對象
4.{list}初始化數組或聚合類

class mycl
{
public:
   int a=0;
   mycl(int i):a(i){cout<<"constructor!"<<endl;}
   mycl(const mycl &c):a(c.a){cout<<"copy constructor!"<<endl;}
   ~mycl(){cout<<"destructor!"<<endl;}
};
mycl fun(const mycl &m)
{
    cout<<"before copy"<<endl;
    mycl mtmp(m);
     cout<<"after copy"<<endl;
    return mtmp;  //這裏返回mtmp到main時
} 
int main()    
{
    int a=0;
    mycl m1(a);
    mycl m2=fun(m1);
    return 0;                
}

在這裏插入圖片描述
通常g++(大多數編譯器)默認開啓RVO(return value optimization),從而看不到構造臨時返回變量的拷貝構造過程,可以通過"-fno-elide-constructors"命令關閉,還原拷貝構造返回值的過程
在這裏插入圖片描述
explict
explicit聲明的函數只能直接初始化

class ClassA
{
public:
explicit ClassA(string s){}  
//explicit只能聲明在class內部 且只能作用於單變量構造函數
//此時無法完成ClassA a("123")中const char*到string的隱式轉換
}
static_cast<ClassA>(s); 
//可以顯示強制轉化使用string構造一個ClassA臨時變量

拷貝構造函數在很多情況下是隱式使用的,因此不能是explicit的
不管有沒有定義,編譯器都會合成拷貝構造函數(synthesized copy constructor):會逐個拷貝數組
但有些情況並不希望某些對象能被拷貝(如istream類)

拷貝賦值運算符

copy-assignment operator:類未定義時編譯器代爲合成
部分運算符重載必須定義爲成員函數(如“=”),左側運算對象隱式綁定this,右側顯示傳遞參數

//等效合成拷貝賦值運算符
C& operator=(const C &r)
{   
    mem=r.mem;    //將每個非static成員賦予左對象成員,操作我自己
    return *this; //返回指向對象本身的引用,返回我自己
}

指向對象的引用或指針離開作用域時,其指向的對象並不會執行析構函數,需要delete
析構函數自身並不直接銷燬成員,成員是在執行完析構函數體後的析構階段被銷燬的

三五法則
拷貝構造+拷貝賦值運算符+析構+移動構造+移動賦值
析構 ⇒(拷貝構造+賦值拷貝構造)= 成套定義
尤其注意:需要析構則一定要 拷貝構造函數 和 賦值拷貝運算符

class HasPtr
{
public:
string *p;
HasPtr(const string &s=string()):p(new string(s)){}
~HasPtr(){delete p;}
}
HasPtr fun(HasPtr h)
{
  HasPtr r=h;  
  //使用了合成拷貝構造和賦值拷貝運算符,指針只是簡單的被拷貝指向同一地址
  return r;    
  //返回值後r和h均被銷燬,但此時析構函數delete p兩次,未定義的錯位結果
}

阻止拷貝

如iostream類,定義多個對象將導致多個對象寫入或讀取相同的IO緩衝
通過"刪除函數(deleted function)"來阻止拷貝

 class C
 {
 public:
     C()=default;
     C(const C &)=delete;  
     //必須出現在第一次聲明的時候=delete(預編譯檢查),防止被使用
     C& operator=(const C &)=delete;
     ~C()=default;    
     //析構函數不能(無法通過編譯)delete,否則無法銷燬對象
 }

當存在不能拷貝、賦值或銷燬的成員時,則類的合成控制函數=delete
C11之前:通過將拷貝構造和賦值運算符聲明爲private但不定義實現阻止拷貝

 class C
 {
     C(const C &); 
     C& operator=(const C &);
 public:
     C()=default;
     ~C()=default; 
 }
能用delete用delete!!! 能用delete用delete!!! 能用delete用delete!!!

移動構造函數

move constructor

移動賦值運算符

move-assignment operator

析構函數

destructor

對象移動

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章