話說,在大學剛接觸C語言時,我們的老師便不斷地!不斷地!!不斷地!!!告訴我們要把main函數分為5個部分:
1、聲明+初始化 2、賦值 3、輸入 4、輸出 5、釋放
我就非常不理解,為什麼非要把『聲明+初始化』以及『賦值』兩步分開。
難道『初始化+賦值』一起會遭受詛咒?
明明教材就有程序是這麼這樣的:int i = 10; 更有甚者,直接int i, j = 1,k = 10; 連聲明加賦值,直接都搞定。
而為什麼老曹總逼我們把別人寫一行的代碼寫成10行……
//initialization
int i = 0
int j = 0;
int k = 0;
//assignment
i = 0;
j = 1;
k = 10;
究竟是為什麼需要將賦值這一步單獨提出來呢?難道是由於這樣做才能防止某種特殊的詛咒??對的!
我今天才發現其中包含了VC6.0包括VS2008中包含著這樣一個“詛咒”~
首先我們寫這樣一段簡單的代碼
[cpp]
class Example
{
private:
int m_data1;
int m_data2;
int * m_pdata3;
public:
Example();
void setData(int given1, int given2, int given3);
};
Example::Example()
{
m_data1 = 0;
m_data2 = 0;
m_pdata3 = new int;
*m_pdata3 = 0;
}
void Example::setData(int given1, int given2, int given3)
{
m_data1 = given1;
m_data2 = given2;
*m_pdata3 = given3;
}
int _tmain(int argc, _TCHAR* argv[])
{
Example e1;
e1.setData(10, 20, 30);
Example e2;
e2 = e1; www.2cto.com
e2.setData(40, 50, 60);
return 0;
}
執行是可以通過的。
在這裡,我們先定義Example類e1;後定義了Example類e2。我們並沒有重載賦值運算符=,然而編譯器卻找到了一種方法使得 e2 = e1 這個運算符得以實現。那麼,可以這麼理解,在重載賦值 = 之前,系統默認創建了一個賦值函數。在這裡,我們暫且把這個賦值函數叫做:默認賦值函數。
下面我們看一下默認賦值函數的功能。
首先,在e1.setData(10, 20, 30)後,監視到e1是這樣的:
當e2 = e1 賦值後,e2的監視窗口如此:
可以看到,e1,e2中包含的指針m_pdata3指向同一片存儲空間。如下的操作更能證明這點:
在e2.setData(40, 50, 60),對e2內部數據進行修改時,會發現e1內部指針對應的內存也進行了改變,變成了60
由此,我們可以推斷,默認賦值函數的功能如下:將賦值符右邊類的成員變量的取值原封不動得賦給左邊的類內同樣的成員變量。
既然清楚了,賦值運算符具有默認賦值函數。
下面我們手工給Example類重載賦值函數。
[cpp]
Example& Example::operator=(const Example& e)
{
m_data1 = e.m_data1;
m_data2 = e.m_data2;
m_pdata3 = new int;
*m_pdata3 = *(e.m_pdata3);
return *this;
}
當然,我們不希望改動e2指針內的數據,e1發生同樣變化。換言之本類對象開辟的空間,歸此類對象獨有。因此,在重載時,開辟了空間後,將上一個空間中的值賦給新空間。
主函數不變:
[cpp]
int _tmain(int argc, _TCHAR* argv[])
{
Example e1;
e1.setData(10, 20, 30);
Example e2;
e2 = e1;
e2.setData(40, 50, 60);
return 0;
}
當執行完畢e2.setData(40, 50, 60)後,通過監視窗口,我們會發現:
果然,e2的開辟的空間變成了60,而e1沒有變化。目的實現。
好了,有了這些儲備,相信你會很容易理解這個“詛咒”了:
當你把剛剛主函數的兩句
Example e2; //初始化
e2 = e1; //賦值
中的賦值與初始化合二為一,即寫成Example e2 = e1後
主函數成為這樣:
[cpp]
int _tmain(int argc, _TCHAR* argv[])
{
Example e1;
e1.setData(10, 20, 30);
Example e2 = e1;
e2.setData(40, 50, 60);
return 0;
}
讓我們看看程序執行結果:
我們會發現,e2沒有獨立開辟空間,而是指向了e1的空間。
原來賦值與初始化合一時,調用的 = 運算符時,執行的不是重載的 = 運算符函數,而是默認賦值函數!
這就是那個將初始化與賦值合二為一的詛咒啊!不加區分也許有一天就會造成意想不到的問題!