一、複製構造函數的概念與作用
在C++中,複製構造函數是一種特殊的構造函數,它的作用是用一個已存在的對象來初始化一個新對象。複製構造函數通常用於對象複製、對象傳遞等情況中。在C++中,如果我們沒有為一個類定義複製構造函數,編譯器會自動生成默認的複製構造函數,但它只是進行簡單的位拷貝,即僅僅將一個對象的位元組序列複製到另一個對象中,因此可能會出現一些意料之外的問題。自定義複製構造函數可以保證複製出來的對象的正確性,因為它是由程序員編寫的,能夠更好地滿足用戶的需求。
#include #include using namespace std; class Student{ public: Student(); //構造函數 Student(const Student &stu); //複製構造函數 ~Student(); //析構函數 void SetName(char *pName); char *GetName(); void SetAge(int age); int GetAge(); private: int m_age; char *m_name; }; Student::Student(){ m_age = 0; m_name = new char[1]; *m_name = '\0'; } Student::Student(const Student &stu){ m_age = stu.m_age; m_name = new char[strlen(stu.m_name)+1]; strcpy(m_name,stu.m_name); } Student::~Student(){ delete [] m_name; } void Student::SetName(char *pName){ if(m_name) delete [] m_name; m_name = new char[strlen(pName)+1]; strcpy(m_name,pName); } char *Student::GetName(){ return m_name; } void Student::SetAge(int age){ m_age = age; } int Student::GetAge(){ return m_age; } int main() { Student stu1; stu1.SetName("Tom"); stu1.SetAge(20); Student stu2(stu1);//調用複製構造函數 cout<<"stu2'name:"<<stu2.GetName()<<",stu2'age:"<<stu2.GetAge()<<endl; return 0; }
二、複製構造函數的實現方式
自定義複製構造函數的實現方式有兩種:淺拷貝和深拷貝。
淺拷貝:淺拷貝只是簡單地將一個對象的數據成員複製到另一個對象中,如果數據成員中有指針類型的變數,複製後兩個對象將共享同一塊內存空間,這樣會導致一個對象釋放該空間後,另一個對象就失去了對它的訪問權,可能會導致程序崩潰。
深拷貝:深拷貝不僅要將一個對象的數據成員複製到另一個對象中,還要為指針類型的數據成員申請一塊內存空間,再將原對象的指針指向的內存空間內容複製到新的內存空間中,這樣兩個對象就不會共享同一塊內存空間。
//淺拷貝 Student::Student(const Student &stu){ m_age = stu.m_age; m_name = stu.m_name; } //深拷貝 Student::Student(const Student &stu){ m_age = stu.m_age; m_name = new char[strlen(stu.m_name)+1]; strcpy(m_name,stu.m_name); }
三、複製構造函數在對象傳遞中的應用
使用複製構造函數可以簡化對象傳遞的過程,將對象作為參數傳遞給函數時,如果不使用複製構造函數,函數會在棧中重新為對象分配內存空間,造成內存空間的浪費;如果使用複製構造函數,不僅可以減少內存的浪費,而且能夠更好地保護對象的數據。
void DoSomething(Student stu){ //stu為參數對象 stu.SetName("Jerry"); //修改stu的成員變數 } int main() { Student stu; stu.SetName("Tom"); DoSomething(stu); //stu作為參數傳遞給函數DoSomething() cout<<"stu'name:"<<stu.GetName()<<endl; //輸出「Tom」 return 0; }
四、常見問題及解決辦法
問題1:複製構造函數被無限遞歸調用的問題。
原因:如果複製構造函數的函數體中使用的是該類對象,而非該類對象的地址,則會導致複製構造函數被無限遞歸調用。
解決辦法:將複製構造函數中使用的參數改為該類對象的引用。
//錯誤的複製構造函數 Student::Student(Student stu){ m_age = stu.m_age; //使用stu對象 m_name = stu.m_name; //使用stu對象 } //正確的複製構造函數 Student::Student(const Student &stu){ //使用stu對象的引用 m_age = stu.m_age; m_name = new char[strlen(stu.m_name)+1]; strcpy(m_name,stu.m_name); }
問題2:複製構造函數的影響範圍問題。
原因:如果複製構造函數中包含有動態分配內存的操作,但該類對象在程序中沒有被使用複製構造函數初始化,釋放該對象時會出現內存泄漏。
解決辦法:在類中定義複製構造函數的同時,也要定義相應的拷貝賦值運算符(operator=),確保對象的複製和對象的賦值均正確無誤。
class Student{ public: Student(); Student(const Student &stu); Student & operator=(const Student &stu); //拷貝賦值運算符 ~Student(); ... }; Student & Student::operator=(const Student &stu){ if(this != &stu){ //避免自賦值 m_age = stu.m_age; delete [] m_name; m_name = new char[strlen(stu.m_name)+1]; strcpy(m_name,stu.m_name); } return *this; }
五、總結
複製構造函數是C++中一個非常重要且基礎的概念,它可以保證對象複製的正確性,實現對象傳遞時的內存優化,避免不必要的內存浪費和內存泄漏。自定義複製構造函數需要注意淺拷貝和深拷貝的區別,同時也需要考慮對象傳遞和對象賦值時的問題,只有多方面思考,才能編寫出更加完善的複製構造函數。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/235683.html