下面講如何使用C++類構造函數,所謂C++類是定義同一類所有對象的變量和方法的藍圖或原型,C++類體內的成員是需要開辟動態開辟堆內存的,如果我們不自定義拷貝構造函數而讓系統自己處理。
我們已經學習過了類的構造函數和析構函數的相關知識,對於普通類型的對象來說,他們之間的復制是很簡單的,例如: 自己定義的類的對象同樣是對象,誰也不能阻止我們用以下的方式進行復制,例如:
- #include <iostream>
- using namespace std;
- class Test
- {
- public:
- Test(int temp)
- {
- p1=temp;
- }
- protected:
- int p1;
- };
- void main()
- {
- Test a(99);
- Test b=a;
- }
普通對象和類對象同為對象,他們之間的特性有相似之處也有不同之處,C++類對象內部存在成員變量,而普通對象是沒有的,當同樣的復制方法發生在不同的對象上的時候,那麼系統對他們進行的操作也是不一樣的,就C++類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個復制過程的。
在上面的代碼中,我們並沒有看到拷貝構造函數,同樣完成了復制工作,這又是為什麼呢?因為當一個類沒有自定義的拷貝構造函數的時候系統會自動提供一個默認的拷貝構造函數,來完成復制工作。
下面,我們為了說明情況,就普通情況而言(以上面的代碼為例),我們來自己定義一個與系統默認拷貝構造函數一樣的拷貝構造函數,看看它的內部是如何工作的!
代碼如下:
- #include <iostream>
- using namespace std;
- class Test
- {
- public:
- Test(int temp)
- {
- p1=temp;
- }
- Test(Test &c_t)//這裡就是自定義的拷貝構造函數
- {
- cout<<"進入copy構造函數"<<endl;
- p1=c_t.p1;//這句如果去掉就不能完成復制工作了,此句復制過程的核心語句
- }
上面代碼中的Test(Test &c_t)就是我們自定義的拷貝構造函數,拷貝構造函數的名稱必須與C++類名稱一致,函數的形式參數是本C++類型的一個引用變量,且必須是引用。 當用一個已經初始化過了的自定義C++類類型對象去初始化另一個新構造的對象的時候。
拷貝構造函數就會被自動調用,如果你沒有自定義拷貝構造函數的時候系統將會提供給一個默認的拷貝構造函數來完成這個過程。上面代碼的復制核心語句就是通過Test(Test &c_t)拷貝構造函數內的p1=c_t.p1;語句完成的。
如果取掉這句代碼,那麼b對象的p1屬性將得到一個未知的隨機值;就上面的代碼情況而言,很多人會問到,既然系統會自動提供一個默認的拷貝構造函數來處理復制,那麼我們沒有意義要去自定義拷貝構造函數呀。
對,就普通情況而言這的確是沒有必要的,但在某寫狀況下,C++類體內的成員是需要開辟動態開辟堆內存的,如果我們不自定義拷貝構造函數而讓系統自己處理,那麼就會導致堆內存的所屬權產生混亂,試想一下,已經開辟的一端堆地址原來是屬於對象a的。
由於復制過程發生,b對象取得是a已經開辟的堆地址,一旦程序產生析構,釋放堆的時候,計算機是不可能清楚這段地址是真正屬於誰的,當連續發生兩次析構的時候就出現了運行錯誤。