C++面向?qū)ο螅荷钊虢馕鲱惖臉?gòu)造函數(shù)與拷貝控制
C++作為一門強(qiáng)大的編程語言,在面向?qū)ο缶幊蹋∣OP)領(lǐng)域占據(jù)著舉足輕重的地位。在C++的OOP中,類(Class)是基礎(chǔ),而構(gòu)造函數(shù)和拷貝控制則是實現(xiàn)類實例創(chuàng)建、初始化和復(fù)制的核心機(jī)制。
1.無參構(gòu)造函數(shù)
無參構(gòu)造函數(shù)是類的一個特殊成員函數(shù),它在創(chuàng)建類的新對象時被自動調(diào)用,用于初始化對象的數(shù)據(jù)成員。當(dāng)定義一個類時,如果沒有顯式定義任何構(gòu)造函數(shù),編譯器會自動生成一個默認(rèn)的無參構(gòu)造函數(shù)。這個默認(rèn)構(gòu)造函數(shù)通常執(zhí)行一些基本的初始化操作。
class MyClass {
public:
MyClass() {
// 無參構(gòu)造函數(shù)體
}
};
在上面的例子中,MyClass是一個類,它有一個無參構(gòu)造函數(shù)。當(dāng)創(chuàng)建MyClass的實例時,如MyClass obj;,這個無參構(gòu)造函數(shù)將被調(diào)用。
2、帶參構(gòu)造函數(shù)
帶參構(gòu)造函數(shù)允許我們在創(chuàng)建對象時傳遞參數(shù),根據(jù)傳遞的參數(shù)初始化對象的數(shù)據(jù)成員。帶參構(gòu)造函數(shù)可以有多個,只要每個構(gòu)造函數(shù)的參數(shù)列表不同即可。
class MyClass {
private:
int value;
public:
MyClass(int val) : value(val) {
// 帶參構(gòu)造函數(shù)體
}
};
在這個例子中,MyClass有一個帶參數(shù)val的構(gòu)造函數(shù)。當(dāng)創(chuàng)建對象時,如MyClass obj(10);,傳遞的參數(shù)10將被用來初始化value數(shù)據(jù)成員。
3.拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)用于創(chuàng)建一個對象并將其初始化為另一個同類對象的副本。拷貝構(gòu)造函數(shù)通常在以下情況下被調(diào)用:
- 當(dāng)用一個已存在的對象初始化新對象時。
- 當(dāng)函數(shù)的參數(shù)是類對象時,會使用拷貝構(gòu)造函數(shù)傳遞實參的副本。
- 當(dāng)函數(shù)的返回值是類對象時,會使用拷貝構(gòu)造函數(shù)復(fù)制返回值。
如果程序員沒有顯式定義拷貝構(gòu)造函數(shù),編譯器會自動生成一個。編譯器生成的拷貝構(gòu)造函數(shù)執(zhí)行的是淺拷貝。
class MyClass {
private:
int* data;
public:
MyClass(const MyClass& other) {
// 拷貝構(gòu)造函數(shù)體
data = new int(*other.data); // 深拷貝
}
};
在上面的例子中,MyClass有一個拷貝構(gòu)造函數(shù),它通過深拷貝來復(fù)制other對象的數(shù)據(jù)成員。
4.深拷貝與淺拷貝
淺拷貝和深拷貝是拷貝構(gòu)造函數(shù)執(zhí)行的兩種不同的復(fù)制方式:
- 淺拷貝:簡單地復(fù)制對象的成員變量,包括指針成員。如果指針成員指向了動態(tài)分配的內(nèi)存,那么淺拷貝會導(dǎo)致兩個對象共享同一塊內(nèi)存,可能會引發(fā)諸如內(nèi)存泄漏、數(shù)據(jù)不一致等問題。
- 深拷貝:復(fù)制對象的所有成員變量,并且復(fù)制指針成員指向的動態(tài)分配的內(nèi)存。這樣每個對象都有自己的內(nèi)存副本,避免了上述問題。
在實際應(yīng)用中,如果類中有指針成員,通常需要自定義拷貝構(gòu)造函數(shù)來實現(xiàn)深拷貝。
下面分別給出一個深拷貝和淺拷貝的例子,以便更好地理解這兩種拷貝方式的區(qū)別。
為了展示深拷貝和淺拷貝在內(nèi)存分配上的不同,打印出拷貝前后對象的內(nèi)存地址。這樣我們可以清楚地看到,淺拷貝會導(dǎo)致兩個對象共享相同的內(nèi)存地址,而深拷貝則會使每個對象擁有自己的內(nèi)存地址。
淺拷貝例子:
#include <iostream>
class ShallowCopy {
public:
int* data;
// 構(gòu)造函數(shù)
ShallowCopy(int val) {
data = new int(val);
std::cout << "原始對象中 data 的地址是: " << data << std::endl;
}
// 拷貝構(gòu)造函數(shù)(淺拷貝)
ShallowCopy(const ShallowCopy& other) {
data = other.data; // 淺拷貝,只是復(fù)制了指針地址
std::cout << "淺拷貝對象中 data 的地址是: " << data << std::endl;
}
// 析構(gòu)函數(shù)
~ShallowCopy() {
//delete data; // 釋放內(nèi)存 如果不注釋的話,會被釋放兩次報錯
std::cout << "內(nèi)存地址 " << data << " 被釋放" << std::endl;
}
};
int main() {
ShallowCopy obj1(10);
ShallowCopy obj2(obj1); // 使用拷貝構(gòu)造函數(shù)進(jìn)行淺拷貝
return 0;
}
在這個例子中,我們打印了原始對象和淺拷貝對象的data指針的內(nèi)存地址。由于淺拷貝只是復(fù)制了指針,所以兩個對象的data指針指向了相同的內(nèi)存地址。
深拷貝例子:
#include <iostream>
class DeepCopy {
public:
int* data;
// 構(gòu)造函數(shù)
DeepCopy(int val) {
data = new int(val);
std::cout << "原始對象中 data 的地址是: " << data << std::endl;
}
// 拷貝構(gòu)造函數(shù)(深拷貝)
DeepCopy(const DeepCopy& other) {
data = new int(*other.data); // 深拷貝,復(fù)制指針指向的值
std::cout << "深拷貝對象中 data 的地址是: " << data << std::endl;
}
// 析構(gòu)函數(shù)
~DeepCopy() {
delete data; // 釋放內(nèi)存
std::cout << "內(nèi)存地址 " << data << " 被釋放" << std::endl;
}
};
int main() {
DeepCopy obj1(10);
DeepCopy obj2(obj1); // 使用拷貝構(gòu)造函數(shù)進(jìn)行深拷貝
return 0;
}
在這個例子中,我們同樣打印了原始對象和深拷貝對象的data指針的內(nèi)存地址。由于深拷貝復(fù)制了指針指向的值,并為新的對象分配了新的內(nèi)存,所以兩個對象的data指針指向了不同的內(nèi)存地址。
運(yùn)行這兩個程序,我們可以觀察到淺拷貝和深拷貝在內(nèi)存分配上的不同。在淺拷貝的情況下,兩個對象的data指針指向相同的內(nèi)存地址;而在深拷貝的情況下,每個對象的data指針指向不同的內(nèi)存地址。
5.總結(jié)
通過本文的介紹,我們了解了C++中構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)的作用、特點和性質(zhì)。構(gòu)造函數(shù)用于初始化對象的數(shù)據(jù)成員,在對象創(chuàng)建時被調(diào)用;而拷貝構(gòu)造函數(shù)則用于創(chuàng)建對象的副本,在對象復(fù)制時被調(diào)用。
在實現(xiàn)拷貝構(gòu)造函數(shù)時,我們需要注意深拷貝和淺拷貝的區(qū)別,特別是在處理動態(tài)分配內(nèi)存的情況下,以避免出現(xiàn)內(nèi)存泄漏和懸掛指針等問題。