一、複製構造函數的概念與作用
在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-hant/n/235683.html
微信掃一掃
支付寶掃一掃