A(const A&); //默認拷貝構造函數
A& operater = (const A& a); //默認賦值函數
【弊端】:若類中包含指針成員或引用成員,這兩個默認的函數可能隱含錯誤。
(1) 原有的內存沒有釋放,造成內存泄漏;
(2) 使兩個對象中的指針成員指向同一塊內存,任何一方變動都會影響另一方;
(3) 在對象析構時,delete兩次。
拷貝構造函數:在創建對象,並用另一個已經存在的對象來初始化它時調用。
如:String a("hello"); //調用帶參數的構造函數
String b(a); //或是String b = a,調用拷貝構造函數
賦值函數:把一個對象賦值給另一個已經存在的對象,使得已經存在的那個對象和源對象具有相同的狀態。
如:String c;
c = a;
類String拷貝構造函數在函數入口處不用與NULL比較,因爲引用不可能是NULL,而指針可以爲NULL,所以在默認構造函數中需與NULL比較。
例1:類String的拷貝構造函數和拷貝賦值函數
String::String(const String& other)
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
m_size = length;
}
String& String::operator = (const String& other)
{
//(1)檢查自賦值
if (this != &other) //地址相等才認爲是同一對象,不能錯寫成if (*this = other)
{
//(2)分配新的內存空間,並拷貝內容
char* temp = new char[strlen(other.m_data) + 1];
strcpy(temp, other.m_data ); //連'/0'一起拷貝
//(3)釋放原有的內存資源
delete [ ] m_data;
m_data = temp;
m_size = strlen(other.m_data);
}
//(4)返回本對象的引用
return *this; //返回本對象的引用,目的是爲了實現a=b=c這樣的鏈式表達式
}
如果先把原有的內存釋放,如果後來內存重分配失敗,就慘了!所以,先分配內存給一個臨時指針,萬一分配失敗也不會改變this對象。
如果我們不想編寫拷貝構造函數和拷貝賦值函數(不想拷貝對象),又不允許使用編譯器自動生成的默認函數,只需將拷貝構造函數和拷貝賦值函數聲明爲private,並且不實現它們。
基類的構造函數、析構函數和賦值函數不能被派生類繼承。如果存在類繼承關係,則在編寫基本函數時應該注意:
(1) 派生類的構造函數應該其初始化列表裏顯式地調用基類構造函數;
(2) 如果基類是多態類(包括虛函數的類),則必須把基類的析構函數定義爲虛函數,這樣可以像其他函數一樣實現動態綁定,否則可能會造成內存泄漏;
如:Base *pB = new Derived;
是虛函數,析構時先調用Derived::~Derived(),再調用Base::~Base()
不是虛函數,則直接調用Base::~Base(),因此派生類對象的內存不會釋放,造成內存泄漏
(3) 在編寫派生類的賦值函數時,要記得對基類的數據成員重新賦值,可以通過調用基類的賦值函數來實現。
如:
Derived& Derived::operater = (const Derived& other)
{
//(1)檢查自賦值
if (this != &other)
{
//(2)對基類的數據成員重新賦值
Base::operator = (other);
//(3)對派生類的數據成員賦值
m_x = other.m_x;
m_y = other.m_y;
m_z = other.m_z;
}
//(4)返回本對象的引用
return *this;
}
------------------------
文章轉自互聯網。。。