都是指標資料成員“惹的禍”
類中指標資料成員的處理
一般而言,類中只含有內建型別時,只使用編譯器提供的預設constructor函式、預設destructor和預設overloaded assignment operator(過載操作符)即可,但是一旦有了指標資料成員,具體的說是指向堆中的值的指標資料成員,就得另當別論了。
由於編譯器新增的預設函式都比較簡單,對於比較簡單的類而言,通常沒有什麼問題,但是當類中有資料成員指向堆中的值時,什麼都需要程式設計師自己做了。
“實踐出真知”,現在拿出一個比較好的程式碼分析:
這是一個簡單的animal的類,只有兩個資料成員,但其中又一個是string*型別的,它是我們討論的物件。
//Heap Data Member
//Demonstrates an object with a dynamically allocated data member
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal(const string& name = "", int age = 0);
~Animal(); //destructor prototype
Animal(const Animal& c); //copy constructor prototype
Animal& Animal::operator=(const Animal& c); //assignment operator
void Greet() const;
private:
string* m_pName; //★要注意的關鍵
int m_Age;
};
Animal::Animal(const string& name, int age)
{
cout << "(建構函式被呼叫)\n";
m_pName = new string(name); //另外分配記憶體空間
m_Age = age;
}
Animal::~Animal() //手工定義解構函式的必要性
{
cout << "(解構函式被呼叫)\n";
delete m_pName; //釋放記憶體空間
}
Animal::Animal(const Animal& c) //手工定義拷貝建構函式的必要性
{
cout << "(拷貝建構函式被呼叫)\n";
m_pName = new string(*(c.m_pName)); //實現“深拷貝”的關鍵
m_Age = c.m_Age;
}
Animal& Animal::operator=(const Animal& c) //手工定義操作符過載的必要性
{
cout << "(過載操作符被呼叫)\n";
if (this != &c)
{
delete m_pName;//別忘了釋放掉原先的記憶體
m_pName = new string(*(c.m_pName));
m_Age = c.m_Age;
}
return *this;
}
void Animal::Greet() const
{
cout << "你好,我叫" << *m_pName << " ,我今年" << m_Age << "歲了。 ";
cout << "&m_pName: " << cout << &m_pName << endl;
}
//宣告3個測試函式
void testDestructor();
void testCopyConstructor(Animal aCopy);
void testAssignmentOp();
int main()
{
testDestructor();
cout << endl;
Animal crit("Poochie", 5);
crit.Greet();
testCopyConstructor(crit);
crit.Greet();
cout << endl;
testAssignmentOp();
return 0;
}
void testDestructor()
{
Animal toDestroy("Rover", 3);
toDestroy.Greet();
} //執行結束時,toDestroy物件隨即被銷燬
void testCopyConstructor(Animal aCopy) //注:不是引用型別
{
aCopy.Greet();
}
void testAssignmentOp()
{
Animal ani1("ani1", 7);
Animal ani2("ani2", 9);
ani1 = ani2;
ani1.Greet();
ani2.Greet();
cout << endl;
Animal ani3("ani", 11);
ani3 = ani3;
ani3.Greet();
}
執行結果:
可以發現:Animal物件被銷燬、複製以及相互賦值是,對這些資料成員做出了不同的處理,具體分析如下。
解構函式
當物件的資料成員指向堆中的值時,可能產生的問題就是記憶體洩露。如果不編寫自己的解構函式,則編譯器會替程式設計師建立一個預設解構函式,但她並不嘗試釋放掉任何資料成員指向的堆中的記憶體。當類中有資料成員指向堆中值時,則應當編寫自己的解構函式,以便能在物件消失以前釋放掉與物件相關的堆中記憶體,避免記憶體洩露。所以在這裡的解構函式必須對申請的堆中記憶體做相應處理:
delete m_pName; //釋放記憶體空間
main函式中呼叫testDestructor()時測試了解構函式。它建立了一個toDestroy的物件,並且列印出m_pName中的堆中字元的地址。當testDestructor()呼叫完畢要返回main時,自動呼叫了解構函式,釋放掉toDestroy物件佔用的記憶體,包括“Rover”字串佔用的堆中記憶體。解構函式對m_Age沒有做任何處理,這完全沒有問題,因為m_Age不在堆中,而是toDestroy的一部分,並且會隨Animal物件的其餘部分被妥善的處理。
總之:如果在堆中分配記憶體,則應當編寫解構函式來清理與釋放堆中的記憶體。
拷貝建構函式
同建構函式和解構函式一樣簡單,編譯前生成的預設拷貝建構函式只是簡單地將每個資料成員的值複製給新物件同名資料成員,即按成員逐項進行復制。但在我們這個函式中卻不能在使用預設拷貝建構函式,原因還是m_pName的存在。如果只是用預設拷貝建構函式,物件的自動複製將會導致新的物件指向堆中的同一個字串,因為新物件的指標僅僅獲得儲存在原始物件的指標中地址的一個副本。這就造成了資料的淺拷貝。真正需要的拷貝建構函式是能讓新生成的物件在堆中擁有自己的記憶體塊,物件中的每個資料成員都指向一個隊中的物件,這就是深拷貝。所以在Animal類的預設拷貝建構函式需要對m_pName成員分配新的堆記憶體。
m_pName = new string(*(c.m_pName));
觀察程式,發現當testCopyConstructor()時使用的m_pName堆中地址與main中crit使用的m_pName地址並不相同,這就實現了堆記憶體資料的複製。當testCopyConstructor()執行結束時,解構函式被呼叫,釋放了物件記憶體。
總之:當類的資料成員指向堆中記憶體時,應當考慮編寫手動拷貝建構函式來為新物件分配記憶體,實現深拷貝。
賦值運算子的過載
同上述幾個函式一樣,如果程式設計師沒有編寫自己的賦值運算子成員函式,編譯器就會為程式設計師提供一個預設的成員函式,但這也是相當簡單的。
如果只是用預設的賦值運算子成員函式,也只是實現的淺拷貝。所以Animal的賦值運算子成員函式應當寫成:
Animal& Animal::operator=(const Animal& c) //手工定義操作符過載的必要性
{
cout << "(過載操作符被呼叫)\n";
if (this != &c)
{
delete m_pName;//別忘了釋放掉原先的記憶體
m_pName = new string(*(c.m_pName));
m_Age = c.m_Age;
}
return *this;
}
main中的testAssignmentOp()測試了賦值運算子的過載。
總之:當類中有資料成員指向堆中記憶體時,應當考慮為該類過載賦值運算子。
如需轉載,請註明出處:http://write.blog.csdn.net/postedit/8046379
相關文章
- 都是髒資料惹的禍
- 都是標量子查詢惹的禍
- 都是weblogic和ejb惹的禍Web
- XP系統故障 都是“防火牆”惹的禍(轉)防火牆
- 都是crosscheck惹的禍,備份歸檔失敗ROS
- C++ 成員資料指標成員函式指標簡單測試C++指標函式
- 成員方法的this指標指標
- C++的成員指標C++指標
- 城市擁堵加劇,都是網際網路快車惹的禍?
- ###都是設計模式惹的禍-----下面不知道該怎麼寫了###設計模式
- C++ 類成員指標C++指標
- [C++] 成員函式指標和函式指標C++函式指標
- 如何使用成員函式指標函式指標
- 都是密碼惹的禍——由windows server 2008忘記密碼想到的密碼WindowsServer
- 類成員的指標 classname::*member, (classname::*pf)()指標
- UML已死?其實是敏捷惹的禍?敏捷
- c++智慧指標中的reset成員函式C++指標函式
- C++智慧指標作為成員變數C++指標變數
- 指向常量資料的指標和常量指標指標
- 美國大選在即,希拉蕊“郵件門”惹禍
- Oracle RAC啟動失敗-軟連結惹的禍Oracle
- 解決windows2003 sp1“資料執行保護”惹的禍Windows
- 函式指標使用c++類成員函式函式指標C++
- C++ 成員函式指標簡單測試C++函式指標
- 第3周課後實踐·閱讀程式(2)-利用指標訪問私有資料成員指標
- 常資料成員的深複製,const+字元型指標 ,如何寫深複製的程式碼?字元指標
- 淺談C++指標直接呼叫類成員函式C++指標函式
- 無人機滿世界惹禍各國紛紛立法應對無人機
- C++學習day18之 指向類成員的指標C++指標
- 貸款3萬滾成70萬!大二男生網上貸款買iPhone惹禍iPhone
- 比如,一級指標、二級指標等,通過不同層級的指標資料指標
- 解決PHP程序 CPU 100% -- file_get_contents惹的禍PHP
- 靜態資料成員和靜態成員函式函式
- 輕食外賣的資料指標指標
- 挖掘資料價值,指標中臺成為神奇傳送門?指標
- 資料分析——指標理論指標
- 電商運營資料指標指標
- cosnt成員資料的初始化