1、構造函數
拷貝構造函數::
Complex(const Complex
& c)
{
// 將對象c中的數據成員值復制過來
m_real = c.m_real;
m_img = c.m_img;
}
用法::
Complex a();
Complex b =a;
運算符重載
Complex
&operator=( const Complex
&rhs )
{
// 首先檢測等號右邊的是否就是左邊的對象本,若是本對象本身,則直接返回
if ( this ==
&rhs )
{
return *this;
}
// 復制等號右邊的成員到左邊的對象中
this->m_real
= rhs.m_real;
this->m_imag
= rhs.m_imag;
// 把等號左邊的對象再次傳出
// 目的是為了支持連等
eg: a=b=c 系統首先運行 b=c
// 然後運行 a=
( b=c的返回值,這裡應該是復制c值後的b對象)
return *this;
}
用法::
Complex a,b
b=a;
拷貝構造函數與運算符重載均為深拷貝,而非淺拷貝,除了用法上不同,實際結果是一致的。
淺拷貝::
上面提到,如果沒有自定義復制構造函數,則系統會創建默認的復制構造函數,但系統創建的默認復制構造函數只會執行“淺拷貝”,即將被拷貝對象的數據成員的
值一一賦值給新創建的對象,若該類的數據成員中有指針成員,則會使得新的對象的指針所指向的地址與被拷貝對象的指針所指向的地址相同,delete該指針 時則會導致兩次重復delete而出錯。下面是示例:
【淺拷貝與深拷貝】
#include <iostream.h>
#include <string.h>
class Person
{
public :
// 構造函數
Person(char *
pN)
{
cout << "一般構造函數被調用
!\n";
m_pName = new char[strlen(pN)
+ 1];
//在堆中開辟一個內存塊存放pN所指的字符串
if(m_pName
!= NULL)
{
//如果m_pName不是空指針,則把形參指針pN所指的字符串復制給它
strcpy(m_pName ,pN);
}
}
// 系統創建的默認復制構造函數,只做位模式拷貝
Person(Person & p)
{
//使兩個字符串指針指向同一地址位置
m_pName = p.m_pName;
}
~Person( )
{
delete m_pName;
}
private :
char *
m_pName;
};
void main( )
{
Person man("lujun");
Person woman(man);
// 結果導致 man 和 woman
的指針都指向了同一個地址
// 函數結束析構時
// 同一個地址被delete兩次
}
// 下面自己設計復制構造函數,實現“深拷貝”,即不讓指針指向同一地址,而是重新申請一塊內存給新的對象的指針數據成員
Person(Person & chs);
{
// 用運算符new為新對象的指針數據成員分配空間
m_pName=new char[strlen(p.m_pName)+
1];
if(m_pName)
{
// 復制內容
strcpy(m_pName ,chs.m_pName);
}
// 則新創建的對象的m_pName與原對象chs的m_pName不再指向同一地址了
}
2、析構函數
我們知道,用C++開發的時候,用來做基類的類的析構函數一般都是虛函數。可是,為什麼要這樣做呢?下面用一個小例子來說明:
有下面的兩個類:
class ClxBase
{
public:
ClxBase() {};
virtual ~ClxBase() {};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase
{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
代碼
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
的輸出結果是:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
這個很簡單,非常好理解。
但是,如果把類ClxBase析構函數前的virtual去掉,那輸出結果就是下面的樣子了:
Do something in class ClxDerived!
也就是說,類ClxDerived的析構函數根本沒有被調用!一般情況下類的析構函數裡面都是釋放內存資源,而析構函數不被調用的話就會造成內存洩漏。我想所有的C++程序員都知道這樣的危險性。當然,如果在析構函數中做了其他工作的話,那你的所有努力也都是白費力氣。
所以,文章開頭的那個問題的答案就是--這樣做是為了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。
當然,並不是要把所有類的析構函數都寫成虛函數。因為當類裡面有虛函數的時候,編譯器會給類添加一個虛函數表,裡面來存放虛函數指針,這樣就會增加類的存儲空間。所以,只有當一個類被用來作為基類的時候,才把析構函數寫成虛函數。