程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++-copy constructor、copy-assignment operator、destructor,assignmentoperator

C++-copy constructor、copy-assignment operator、destructor,assignmentoperator

編輯:C++入門知識

C++-copy constructor、copy-assignment operator、destructor,assignmentoperator


對於一個類來說,我們把copy constructor、copy-assignment operator、move constructor、move-assignment operator、destructor統稱為copy control。

今天我們先來聊聊其中的copy constructor、copy-assignment operator的destructor這三個。

 

copy constructor

copy constructor:一個constructor如果他的第一個參數是對類的引用,且其他的參數都有缺省值(default values)則,這是一個copy constructor。

1,第一個參數必須是引用類型,因為當我們把一個object當做參數傳遞給一個方法的非引用變量的時候會自動調用copy constructor方法,如果copy constructor自身的參數就是非引用類型的話,這個方法就會引起無限遞歸調用,然後你的程序就boomshakalaka~~。

2,一般我們會把第一個參數設成const,因為一般情況下不會對其進行修改,除非你另有打算。

3,因為copy constructor在很多情況下是默認調用的,如以下情況,所以一般不會把copy constructor設成explicit。

1 std::string s;
2 std::string s1 = s; //隱式調用了copy constructor
3 std::string s2 = std::string(s1);//顯式調用了copy constructor

 

1 class Foo{
2 public:
3     Foo(const Foo&);
4     //...
5 };

何時發生copy constructor調用

為了弄清這個問題我們需要弄清另外一組概念:direct initialization 和copy initialization。

direct initialization:要求編譯器按照一般的方法匹配(function matching)來選擇要調用的方法。

copy initialization:要求編譯器將右操作數拷貝到左操作數,有必要的話還會進行類型轉換,這個過程會調用copy constructor或者move constructor(本文暫不介紹)。

1 std::string s1("balabala");                       //direct initialization
2 std::string s2(10, 'a');                          //direct initialization
3 std::string s3 = s2;                              //copy initialization
4 std::string s4 = std::string(s3);                 //copy initialization
5 std::string s5 = "const char* converts to string";//copy initialization

 copy initialization發生的情況如下:

1,用=來初始化定義的變量時。

2,把一個object當做參數傳遞給一個方法的非引用變量的時。

3,方法返回一個非引用類型的object時。(返回時會首先生成一個臨時object)

4,用花括號列表初始化一個數組或聚合類(aggregate class)成員時。

由於編譯器帶來的誤解:

現在的編譯器有時候會自動繞過copy constructor即編譯器會把下面這一句話

 std::Book book = "9-9-9-9";//假設Book是一個自定義的類 

換成下面這個

 std::Book book("9-9-9-9"); 

請注意在執行上上面兩句語言是完全不一樣的,第一句會首先調用Book(const char*)構造函數生成一個臨時object然後再調用Book(const Book&)把臨時object復制給book。而第二句話會直接調用Book(const char*)然後完事兒。如果你想驗證他們的區別可以實現Book類並將Book(const Book&)設置成私有方法(防止編譯器自動優化),之後你就會發現第一條語句無法執行了。

 

Copy-assignment operator

copy-assignment operator:寫這個方法就是對=操作符進行重載。

1,copy-assignment operator的返回值一般是對其左操作數(left-hand operand)的引用,這是為了讓object的行為更像內置類型而決定的。

1 class Foo{
2 public:
3     Foo& operator=(const Foo&);
4     //...
5 };

何時發生copy-assignment operator調用

答案很顯然是用到=操作符的時候啊,但是這裡要注意的是

初始化的時候並不會調用copy-assignment operator

初始化的時候並不會調用copy-assignment operator

初始化的時候並不會調用copy-assignment operator

重要的事情說三遍,舉例如下

1 std::string s;
2 std::string s1 = s;    //對s1進行初始化,調用的是copy constructor
3 s1 = s;                //對s1進行賦值,調用的是copy-assignment operator

 

 Destructor

Destructor:destructor有兩個部分,function body和destruction part,前者由類的編寫者寫明需要做的內容,後者是隱式的,不需要程序員關心,在function body執行完後自動執行,會銷毀類的非靜態數據成員。

1,因為Destructor沒有參數,所以它是不能被重載的

1 class Foo{
2 public:
3     ~Foo();
4     //...
5 };

何時發生Destructor調用

1,當超出object 的作用域(scope)時。

2,容器銷毀時(container),裡面的元素(element)也會跟著調用自身的destructor從而銷毀。

3,人為使用delete的時候。

4,由某個表達式創建的臨時變量在這個表達式執行完後將自動調用destructor從而銷毀。

5,類的成員如果自身有destructor,會在這個類銷毀的時候調用自身的destructor。

 

 關於編譯器自動提供的版本(Synthesized)

Synthesized copy constructor:即使我們提供了其他版本的copy constructor,編譯器仍然會提供這個版本的copy constructor給我們,它會依次復制非靜態成員給被創建的object,對數組也能正常工作,對於class類型會調用它們自己的copy constructor。

Synthesized copy-assignment operator:行為和Synthesized copy constructor類似,依次把非靜態成員復制給左操作數。

Synthesized destructor:destructor的function body為空。

 

 關於何時我們需要自定義上述的三個方法

1,當需要destructor時,上述三給方法都是需要的。

2,當需要copy constructor時,copy-assignment operator也是需要的,反之亦然。

而當我們需要刪除自己動態分配的內存時,就要用到destructor。

當我們需要進行深度復制時會用到另外兩個,比如對指針指向的元素進行復制等等。

 

關於delete和default的用法

我們可以用default顯示聲明我們想要用默認版本的copy control,也可以用delete顯示聲明我們完全不需要這類方法來達到禁止這個object進行相關的復制和賦值操作。

1,我們能delete除了destructor以外的所有方法來達到顯示告知這個object不能進行相關操作的目的,delete只能寫在一次聲明出現的地方。

2,我們能對所有有默認版本的函數用default顯示聲明我們需要這個默認版本,default可以寫在方法聲明的地方也可以寫在方法定義的地方。

1 class Foo{
2 public:
3     Foo() = default;                     //顯式說明使用默認版本
4     Foo(const Foo&) = delete;            //delete copy constructor
5     Foo& operator=(const Foo&) = delete; //delete copy-assignment operator
6     ~Foo() = default;                    //顯式說明使用默認版本
7     void myFuntion() = delete;           //delete自己的方法
8     //...
9 };

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved