詳解C++ 拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它在創(chuàng)建對象時,是使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象??截悩?gòu)造函數(shù)通常用于:
- 通過使用另一個同類型的對象來初始化新創(chuàng)建的對象。
- 復(fù)制對象把它作為參數(shù)傳遞給函數(shù)。
- 復(fù)制對象,并從函數(shù)返回這個對象。
如果在類中沒有定義拷貝構(gòu)造函數(shù),編譯器會自行定義一個。如果類帶有指針變量,并有動態(tài)內(nèi)存分配,則它必須有一個拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)的最常見形式如下:
classname (const classname &obj) {
// 構(gòu)造函數(shù)的主體
}
在這里,obj 是一個對象引用,該對象是用于初始化另一個對象的。
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 簡單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針分配內(nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函數(shù)
int main( )
{
Line line(10);
display(line);
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存
下面的實例對上面的實例稍作修改,通過使用已有的同類型的對象來初始化新創(chuàng)建的對象:
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 簡單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針分配內(nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函數(shù)
int main( )
{
Line line1(10);
Line line2 = line1; // 這里也調(diào)用了拷貝構(gòu)造函數(shù)
display(line1);
display(line2);
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時,它會產(chǎn)生下列結(jié)果:
調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存
釋放內(nèi)存
關(guān)于為什么當(dāng)類成員中含有指針類型成員且需要對其分配內(nèi)存時,一定要有總定義拷貝構(gòu)造函數(shù)??
默認(rèn)的拷貝構(gòu)造函數(shù)實現(xiàn)的只能是淺拷貝,即直接將原對象的數(shù)據(jù)成員值依次復(fù)制給新對象中對應(yīng)的數(shù)據(jù)成員,并沒有為新對象另外分配內(nèi)存資源。
這樣,如果對象的數(shù)據(jù)成員是指針,兩個指針對象實際上指向的是同一塊內(nèi)存空間。
在某些情況下,淺拷貝回帶來數(shù)據(jù)安全方面的隱患。
當(dāng)類的數(shù)據(jù)成員中有指針類型時,我們就必須定義一個特定的拷貝構(gòu)造函數(shù),該拷貝構(gòu)造函數(shù)不僅可以實現(xiàn)原對象和新對象之間數(shù)據(jù)成員的拷貝,而且可以為新的對象分配單獨的內(nèi)存資源,這就是深拷貝構(gòu)造函數(shù)。
如何防止默認(rèn)拷貝發(fā)生
聲明一個私有的拷貝構(gòu)造函數(shù),這樣因為拷貝構(gòu)造函數(shù)是私有的,如果用戶試圖按值傳遞或函數(shù)返回該類的對象,編譯器會報告錯誤,從而可以避免按值傳遞或返回對象。
總結(jié):
當(dāng)出現(xiàn)類的等號賦值時,會調(diào)用拷貝函數(shù),在未定義顯示拷貝構(gòu)造函數(shù)的情況下,系統(tǒng)會調(diào)用默認(rèn)的拷貝函數(shù)——即淺拷貝,它能夠完成成員的一一復(fù)制。當(dāng)數(shù)據(jù)成員中沒有指針時,淺拷貝是可行的。但當(dāng)數(shù)據(jù)成員中有指針時,如果采用簡單的淺拷貝,則兩類中的兩個指針將指向同一個地址,當(dāng)對象快結(jié)束時,會調(diào)用兩次析構(gòu)函數(shù),而導(dǎo)致指針懸掛現(xiàn)象。所以,這時,必須采用深拷貝。
深拷貝與淺拷貝的區(qū)別就在于深拷貝會在堆內(nèi)存中另外申請空間來儲存數(shù)據(jù),從而也就解決了指針懸掛的問題。簡而言之,當(dāng)數(shù)據(jù)成員中有指針時,必須要用深拷貝。
以上就是詳解C++ 拷貝構(gòu)造函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于C++ 拷貝構(gòu)造函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VC++實現(xiàn)添加文件關(guān)聯(lián)的方法示例
這篇文章主要介紹了VC++實現(xiàn)添加文件關(guān)聯(lián)的方法,涉及VC++針對注冊表的寫入與VC事件響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
VSCode 配置C++開發(fā)環(huán)境的方法步驟
這篇文章主要介紹了VSCode 配置C++開發(fā)環(huán)境的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03

