當(dāng)前位置:
首頁(yè) >
复制构造函数 与 赋值函数 的区别
發(fā)布時(shí)間:2025/3/21
43
豆豆
生活随笔
收集整理的這篇文章主要介紹了
复制构造函数 与 赋值函数 的区别
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值函數(shù)是每個(gè)類最基本的的函數(shù)。每個(gè)類只有一個(gè)析構(gòu)函數(shù)和一個(gè)賦值函數(shù)。但是有很多構(gòu)造函數(shù)(一個(gè)為復(fù)制構(gòu)造函數(shù),其他為普通構(gòu)造函數(shù)。對(duì)于一個(gè)類A,如果不編寫上述四個(gè)函數(shù),c++編譯器將自動(dòng)為A產(chǎn)生四個(gè)默認(rèn)的函數(shù),即:
??? A(void)??????????????????????????????????? //默認(rèn)無(wú)參數(shù)構(gòu)造函數(shù)
??? A(const A &a)???????????????????????? //默認(rèn)復(fù)制構(gòu)造函數(shù)
??? ~A(void);??????????????????????????????? //默認(rèn)的析構(gòu)函數(shù)
??? A & operator = (const A &a); //默認(rèn)的賦值函數(shù)
既然能自動(dòng)生成函數(shù),為什么還需要自定義?原因之一是“默認(rèn)的復(fù)制構(gòu)造函數(shù)”和"默認(rèn)的賦值函數(shù)“均采用”位拷貝“而非”值拷貝“
位拷貝? v.s.? 值拷貝
為便于說(shuō)明,以自定義String類為例,先定義類,而不去實(shí)現(xiàn)。
復(fù)制代碼
#include <iostream>
using namespace std;
class String ?
{
??? public:
??????? String(void);
??????? String(const String &other);
??????? ~String(void);
??????? String & operator =(const String &other);
??? private:
?
??????? char *m_data;
??????? int val;
};
復(fù)制代碼
位拷貝拷貝的是地址,而值拷貝拷貝的是內(nèi)容。
如果定義兩個(gè)String對(duì)象a, b。當(dāng)利用位拷貝時(shí),a=b,其中的a.val=b.val;但是a.m_data=b.m_data就錯(cuò)了:a.m_data和b.m_data指向同一個(gè)區(qū)域。這樣出現(xiàn)問(wèn)題:
??? a.m_data原來(lái)的內(nèi)存區(qū)域未釋放,造成內(nèi)存泄露
??? a.m_data和b.m_data指向同一塊區(qū)域,任何一方改變,會(huì)影響到另一方
??? 當(dāng)對(duì)象釋放時(shí),b.m_data會(huì)釋放掉兩次
因此
當(dāng)類中還有指針變量時(shí),復(fù)制構(gòu)造函數(shù)和賦值函數(shù)就隱含了錯(cuò)誤。此時(shí)需要自己定義。
結(jié)論
??? 有一種特別常見(jiàn)的情況需要自己定義復(fù)制控制函數(shù):類具有指針哈函數(shù)。
??? 賦值操作符和復(fù)制構(gòu)造函數(shù)可以看成一個(gè)單元,當(dāng)需要其中一個(gè)時(shí),我們幾乎也肯定需要另一個(gè)
??? 三法則:如果類需要析構(gòu)函數(shù),則它也需要賦值操作符和復(fù)制構(gòu)造函數(shù)
注意
??? 如果沒(méi)定義復(fù)制構(gòu)造函數(shù)(別的不管),編譯器會(huì)自動(dòng)生成默認(rèn)復(fù)制構(gòu)造函數(shù)
??? 如果定義了其他構(gòu)造函數(shù)(包括復(fù)制構(gòu)造函數(shù)),編譯器絕不會(huì)生成默認(rèn)構(gòu)造函數(shù)
??? 即使自己寫了析構(gòu)函數(shù),編譯器也會(huì)自動(dòng)生成默認(rèn)析構(gòu)函數(shù)
因此此時(shí)如果寫String s是錯(cuò)誤的,因?yàn)槎x了其他構(gòu)造函數(shù),就不會(huì)自動(dòng)生成無(wú)參默認(rèn)構(gòu)造函數(shù)。
復(fù)制構(gòu)造函數(shù)? v.s.? 賦值函數(shù)
復(fù)制代碼
#include <iostream>
#include <cstring>
using namespace std;
class String ?
{
??? public:
??????? String(const char *str);
??????? String(const String &other);
??????? String & operator=(const String &other);
??????? ~String(void);
??? private:
??????? char *m_data;
};
String::String(const char *str)
{
??? cout << "自定義構(gòu)造函數(shù)" << endl;
??? if (str == NULL)
??? {
??????? m_data = new char[1];
??????? *m_data = '\0';
??? }
??? else
??? {
??????? int length = strlen(str);
??????? m_data = new char[length + 1];
??????? strcpy(m_data, str);
??? }
}
String::String(const String &other)
{
??? cout << "自定義拷貝構(gòu)造函數(shù)" << endl;
??? int length = strlen(other.m_data);
??? m_data = new char[length + 1];
??? strcpy(m_data, other.m_data);
}
String & String::operator=(const String &other)
{
??? cout << "自定義賦值函數(shù)" << endl;
??? if (this == &other)
??? {
??????? return *this;
??? }
??? else
??? {
??????? delete [] m_data;
??????? int length = strlen(other.m_data);
??????? m_data = new char[length + 1];
??????? strcpy(m_data, other.m_data);
??????? return *this;
??? }
}
String::~String(void)
{
??? cout << "自定義析構(gòu)函數(shù)" << endl;
??? delete [] m_data;
}
int main()
{
??? cout << "a(\"abc\")" << endl;
??? String a("abc");
??? cout << "b(\"cde\")" << endl;
??? String b("cde");
?? ?
??? cout << " d = a" << endl;
??? String d = a;
??? cout << "c(b)" << endl;
??? String c(b);
??? cout << "c = a" << endl;
??? c = a;
??? cout << endl;
}
復(fù)制代碼
執(zhí)行結(jié)果
說(shuō)明幾點(diǎn)
1. 賦值函數(shù)中,上來(lái)比較 this == &other 是很必要的,因?yàn)榉乐棺詮?fù)制,這是很危險(xiǎn)的,因?yàn)橄旅嬗衐elete []m_data,如果提前把m_data給釋放了,指針已成野指針,再賦值就錯(cuò)了
2. 賦值函數(shù)中,接著要釋放掉m_data,否則就沒(méi)機(jī)會(huì)了(下邊又有新指向了)
3. 拷貝構(gòu)造函數(shù)是對(duì)象被創(chuàng)建時(shí)調(diào)用,賦值函數(shù)只能被已經(jīng)存在了的對(duì)象調(diào)用
??? 注意:String a("hello"); String b("world");? 調(diào)用自定義構(gòu)造函數(shù)
???????????? String c=a;調(diào)用拷貝構(gòu)造函數(shù),因?yàn)閏一開(kāi)始不存在,最好寫成String c(a);
??? A(void)??????????????????????????????????? //默認(rèn)無(wú)參數(shù)構(gòu)造函數(shù)
??? A(const A &a)???????????????????????? //默認(rèn)復(fù)制構(gòu)造函數(shù)
??? ~A(void);??????????????????????????????? //默認(rèn)的析構(gòu)函數(shù)
??? A & operator = (const A &a); //默認(rèn)的賦值函數(shù)
既然能自動(dòng)生成函數(shù),為什么還需要自定義?原因之一是“默認(rèn)的復(fù)制構(gòu)造函數(shù)”和"默認(rèn)的賦值函數(shù)“均采用”位拷貝“而非”值拷貝“
位拷貝? v.s.? 值拷貝
為便于說(shuō)明,以自定義String類為例,先定義類,而不去實(shí)現(xiàn)。
復(fù)制代碼
#include <iostream>
using namespace std;
class String ?
{
??? public:
??????? String(void);
??????? String(const String &other);
??????? ~String(void);
??????? String & operator =(const String &other);
??? private:
?
??????? char *m_data;
??????? int val;
};
復(fù)制代碼
位拷貝拷貝的是地址,而值拷貝拷貝的是內(nèi)容。
如果定義兩個(gè)String對(duì)象a, b。當(dāng)利用位拷貝時(shí),a=b,其中的a.val=b.val;但是a.m_data=b.m_data就錯(cuò)了:a.m_data和b.m_data指向同一個(gè)區(qū)域。這樣出現(xiàn)問(wèn)題:
??? a.m_data原來(lái)的內(nèi)存區(qū)域未釋放,造成內(nèi)存泄露
??? a.m_data和b.m_data指向同一塊區(qū)域,任何一方改變,會(huì)影響到另一方
??? 當(dāng)對(duì)象釋放時(shí),b.m_data會(huì)釋放掉兩次
因此
當(dāng)類中還有指針變量時(shí),復(fù)制構(gòu)造函數(shù)和賦值函數(shù)就隱含了錯(cuò)誤。此時(shí)需要自己定義。
結(jié)論
??? 有一種特別常見(jiàn)的情況需要自己定義復(fù)制控制函數(shù):類具有指針哈函數(shù)。
??? 賦值操作符和復(fù)制構(gòu)造函數(shù)可以看成一個(gè)單元,當(dāng)需要其中一個(gè)時(shí),我們幾乎也肯定需要另一個(gè)
??? 三法則:如果類需要析構(gòu)函數(shù),則它也需要賦值操作符和復(fù)制構(gòu)造函數(shù)
注意
??? 如果沒(méi)定義復(fù)制構(gòu)造函數(shù)(別的不管),編譯器會(huì)自動(dòng)生成默認(rèn)復(fù)制構(gòu)造函數(shù)
??? 如果定義了其他構(gòu)造函數(shù)(包括復(fù)制構(gòu)造函數(shù)),編譯器絕不會(huì)生成默認(rèn)構(gòu)造函數(shù)
??? 即使自己寫了析構(gòu)函數(shù),編譯器也會(huì)自動(dòng)生成默認(rèn)析構(gòu)函數(shù)
因此此時(shí)如果寫String s是錯(cuò)誤的,因?yàn)槎x了其他構(gòu)造函數(shù),就不會(huì)自動(dòng)生成無(wú)參默認(rèn)構(gòu)造函數(shù)。
復(fù)制構(gòu)造函數(shù)? v.s.? 賦值函數(shù)
復(fù)制代碼
#include <iostream>
#include <cstring>
using namespace std;
class String ?
{
??? public:
??????? String(const char *str);
??????? String(const String &other);
??????? String & operator=(const String &other);
??????? ~String(void);
??? private:
??????? char *m_data;
};
String::String(const char *str)
{
??? cout << "自定義構(gòu)造函數(shù)" << endl;
??? if (str == NULL)
??? {
??????? m_data = new char[1];
??????? *m_data = '\0';
??? }
??? else
??? {
??????? int length = strlen(str);
??????? m_data = new char[length + 1];
??????? strcpy(m_data, str);
??? }
}
String::String(const String &other)
{
??? cout << "自定義拷貝構(gòu)造函數(shù)" << endl;
??? int length = strlen(other.m_data);
??? m_data = new char[length + 1];
??? strcpy(m_data, other.m_data);
}
String & String::operator=(const String &other)
{
??? cout << "自定義賦值函數(shù)" << endl;
??? if (this == &other)
??? {
??????? return *this;
??? }
??? else
??? {
??????? delete [] m_data;
??????? int length = strlen(other.m_data);
??????? m_data = new char[length + 1];
??????? strcpy(m_data, other.m_data);
??????? return *this;
??? }
}
String::~String(void)
{
??? cout << "自定義析構(gòu)函數(shù)" << endl;
??? delete [] m_data;
}
int main()
{
??? cout << "a(\"abc\")" << endl;
??? String a("abc");
??? cout << "b(\"cde\")" << endl;
??? String b("cde");
?? ?
??? cout << " d = a" << endl;
??? String d = a;
??? cout << "c(b)" << endl;
??? String c(b);
??? cout << "c = a" << endl;
??? c = a;
??? cout << endl;
}
復(fù)制代碼
執(zhí)行結(jié)果
說(shuō)明幾點(diǎn)
1. 賦值函數(shù)中,上來(lái)比較 this == &other 是很必要的,因?yàn)榉乐棺詮?fù)制,這是很危險(xiǎn)的,因?yàn)橄旅嬗衐elete []m_data,如果提前把m_data給釋放了,指針已成野指針,再賦值就錯(cuò)了
2. 賦值函數(shù)中,接著要釋放掉m_data,否則就沒(méi)機(jī)會(huì)了(下邊又有新指向了)
3. 拷貝構(gòu)造函數(shù)是對(duì)象被創(chuàng)建時(shí)調(diào)用,賦值函數(shù)只能被已經(jīng)存在了的對(duì)象調(diào)用
??? 注意:String a("hello"); String b("world");? 調(diào)用自定義構(gòu)造函數(shù)
???????????? String c=a;調(diào)用拷貝構(gòu)造函數(shù),因?yàn)閏一開(kāi)始不存在,最好寫成String c(a);
總結(jié)
以上是生活随笔為你收集整理的复制构造函数 与 赋值函数 的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Win7下U盘安装Ubuntu14.04
- 下一篇: 几步实现stm32上面移植mqtt