解讀構(gòu)造函數(shù)的調(diào)用規(guī)則、深拷貝與淺拷貝
1.調(diào)用規(guī)則
默認情況下,C++至少會給一個類添加三個函數(shù):
- 默認構(gòu)造函數(shù)(無參,函數(shù)體為空)
- 默認析構(gòu)函數(shù)(無參,函數(shù)體為空)
- 默認拷貝構(gòu)造函數(shù),對屬性進行值拷貝
調(diào)用規(guī)則:
1.如果用戶定義了有參構(gòu)造函數(shù),C++將不在提供默認無參構(gòu)造函數(shù),但是會提供默認拷貝構(gòu)造函數(shù)。

如上圖,我們給類A提供了一個有參的構(gòu)造函數(shù),所以此時,如果再去主函數(shù)定義一個無參的對象時,編譯器就會提示“類A不存在默認構(gòu)造函數(shù)”,這就說明如果自己定義了有參構(gòu)造函數(shù),那么就不會提供默認的無參構(gòu)造函數(shù)。
#include<iostream>
using namespace std;
class A {
public:
int num;
A(int num) {
this->num = num;
}
};
int main()
{
A a(5);
A b(a);
cout << b.num;
return 0;
}在類中我們只提供了一個有參的構(gòu)造函數(shù),我們在主函數(shù)中定義一個b對象,傳的是對象a,那么它便會走拷貝構(gòu)造的函數(shù),如果能夠輸出b.num的值為5的話,則說明提供了默認的拷貝構(gòu)造函數(shù)。

通過打印b的num,發(fā)現(xiàn)確實是5.那么就會提供默認的構(gòu)造函數(shù)。
2.如果用戶定義拷貝構(gòu)造函數(shù),c++不會提供其他構(gòu)造函數(shù)

可以看到當我們僅在類A中寫了一個拷貝構(gòu)造函數(shù)時,再去主函數(shù)中定義一個無參的對象a時,就會報錯,提示“類A不存在默認構(gòu)造函數(shù)”。
2.深拷貝和淺拷貝問題
淺拷貝:就是簡單的賦值操作
存在的問題:如果有指針指向堆區(qū)內(nèi)存時,不同對象的指針成員指向的是同一塊堆區(qū)內(nèi)存。在對象進行釋放時,該堆區(qū)會被釋放兩次。當一個對象修改堆區(qū)的內(nèi)容時,另一個對象的內(nèi)容也會隨著改變。
深拷貝:申請同樣大小的堆區(qū)內(nèi)存,保證兩個堆區(qū)的內(nèi)容一樣。
#include<iostream>
using namespace std;
class A {
int num;
int* p;
public:
A() {
num = 0;
p = nullptr;
cout << "調(diào)用無參構(gòu)造" << endl;
}
A(int a) {
num = a;
p = new int[num];
cout << "調(diào)用有參構(gòu)造" << endl;
}
~A() {
if (p)delete[]p;
}
};
int main() {
A a(3);
A b = a;
return 0;
}在類A中我們定義了一個指針變量,在有參構(gòu)造函數(shù)中,我們在堆區(qū)開辟了一塊空間。在主函數(shù)數(shù)中,定義對象b要走拷貝構(gòu)造函數(shù)。下面我們來運行這段代碼:

發(fā)現(xiàn)報錯了,原因就是對象a和對象b的成員變量*p指向的是同一塊堆區(qū)內(nèi)存,再調(diào)用析構(gòu)函數(shù)時,對象b先把這塊堆區(qū)內(nèi)存釋放了,當對象a調(diào)用析構(gòu)函數(shù)時,此時那塊堆區(qū)內(nèi)存已經(jīng)不存在了,所以會出現(xiàn)訪問內(nèi)存失敗,導致程序崩潰。
那么解決的辦法就是重寫拷貝構(gòu)造函數(shù),讓他們指向不同的堆區(qū)區(qū)域。代碼如下:
#include<iostream>
using namespace std;
class A {
int num;
int* p;
public:
A() {
num = 0;
p = nullptr;
cout << "調(diào)用無參構(gòu)造" << endl;
}
A(int a) {
num = a;
p = new int[num];
cout << "調(diào)用有參構(gòu)造" << endl;
}
A(const A& other) {//萬能引用,避免實參修改形參
num = other.num;
p = new int[num];//保證內(nèi)存大小相同
for (int i = 0; i < num; i++) {
p[i] = other.p[i];//保證數(shù)據(jù)相同
}
cout << "調(diào)用拷貝構(gòu)造" << endl;
}
~A() {
if (p)delete[]p;
cout << "調(diào)用析構(gòu)函數(shù)" << endl;
}
};
int main() {
A a(3);
A b = a;
return 0;
}通過再在堆區(qū)上開辟一塊內(nèi)存,讓這塊堆區(qū)內(nèi)存上的內(nèi)容和對象a的堆區(qū)內(nèi)容一樣就行,這就是深拷貝。

程序能正常運行。
3.string類的拷貝構(gòu)造練習
可以通過下面這個自定義string類進一步加深對深拷貝的理解。
#include<iostream>
using namespace std;
class String {
int size;
char* p;
public:
String() {
p = nullptr;
size = 0;
}
String(const char* p) {
size = strlen(p);
this->p = new char[size + 1];//\0,所以加一
strcpy_s(this->p, size + 1, p);
}
~String() {
if (p) delete[]p;
}
String(const String& other) {
this->size = other.size;
this->p = new char[size + 1];
strcpy_s(this->p, size + 1,other.p);
}
void print() {
cout << p<<endl;
}
};
int main() {
String str = "abcde";
String str2(str);
str2.print();
return 0;
}總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
c++實現(xiàn)對輸入數(shù)組進行快速排序的示例(推薦)
下面小編就為大家?guī)硪黄猚++實現(xiàn)對輸入數(shù)組進行快速排序的示例(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
使用?Visual?Studio?2022?開發(fā)?Linux?C++?應用程序的過程詳解
Visual?Studio?2022?引入了用于?Linux?C++?開發(fā)的本機?WSL2?工具集,可以構(gòu)建和調(diào)試?Linux?C++?代碼,并提供了非常好的?Linux?文件系統(tǒng)性能、GUI?支持和完整的系統(tǒng)調(diào)用兼容性,這篇文章主要介紹了使用Visual?Studio?2022?開發(fā)?Linux?C++?應用程序,需要的朋友可以參考下2021-11-11
C/C++?Qt?TabWidget?實現(xiàn)多窗體創(chuàng)建詳解
TabWidget組件配合自定義Dialog組件,可實現(xiàn)一個復雜的多窗體分頁結(jié)構(gòu)。這篇文章就主要介紹了如何通過TabWidget實現(xiàn)多窗體的創(chuàng)建,感興趣的小伙伴可以了解一下2021-12-12

