复制构造函数 与 赋值函数 的区别
生活随笔
收集整理的這篇文章主要介紹了
复制构造函数 与 赋值函数 的区别
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
構造函數、析構函數、賦值函數是每個類最基本的的函數。每個類只有一個析構函數和一個賦值函數。但是有很多構造函數(一個為復制構造函數,其他為普通構造函數。對于一個類A,如果不編寫上述四個函數,c++編譯器將自動為A產生四個默認的函數,即:
??? A(void)??????????????????????????????????? //默認無參數構造函數
??? A(const A &a)???????????????????????? //默認復制構造函數
??? ~A(void);??????????????????????????????? //默認的析構函數
??? A & operator = (const A &a); //默認的賦值函數
既然能自動生成函數,為什么還需要自定義?原因之一是“默認的復制構造函數”和"默認的賦值函數“均采用”位拷貝“而非”值拷貝“
位拷貝? v.s.? 值拷貝
為便于說明,以自定義String類為例,先定義類,而不去實現。
復制代碼
#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;
};
復制代碼
位拷貝拷貝的是地址,而值拷貝拷貝的是內容。
如果定義兩個String對象a, b。當利用位拷貝時,a=b,其中的a.val=b.val;但是a.m_data=b.m_data就錯了:a.m_data和b.m_data指向同一個區域。這樣出現問題:
??? a.m_data原來的內存區域未釋放,造成內存泄露
??? a.m_data和b.m_data指向同一塊區域,任何一方改變,會影響到另一方
??? 當對象釋放時,b.m_data會釋放掉兩次
因此
當類中還有指針變量時,復制構造函數和賦值函數就隱含了錯誤。此時需要自己定義。
結論
??? 有一種特別常見的情況需要自己定義復制控制函數:類具有指針哈函數。
??? 賦值操作符和復制構造函數可以看成一個單元,當需要其中一個時,我們幾乎也肯定需要另一個
??? 三法則:如果類需要析構函數,則它也需要賦值操作符和復制構造函數
注意
??? 如果沒定義復制構造函數(別的不管),編譯器會自動生成默認復制構造函數
??? 如果定義了其他構造函數(包括復制構造函數),編譯器絕不會生成默認構造函數
??? 即使自己寫了析構函數,編譯器也會自動生成默認析構函數
因此此時如果寫String s是錯誤的,因為定義了其他構造函數,就不會自動生成無參默認構造函數。
復制構造函數? v.s.? 賦值函數
復制代碼
#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 << "自定義構造函數" << 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 << "自定義拷貝構造函數" << 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 << "自定義賦值函數" << 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 << "自定義析構函數" << 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;
}
復制代碼
執行結果
說明幾點
1. 賦值函數中,上來比較 this == &other 是很必要的,因為防止自復制,這是很危險的,因為下面有delete []m_data,如果提前把m_data給釋放了,指針已成野指針,再賦值就錯了
2. 賦值函數中,接著要釋放掉m_data,否則就沒機會了(下邊又有新指向了)
3. 拷貝構造函數是對象被創建時調用,賦值函數只能被已經存在了的對象調用
??? 注意:String a("hello"); String b("world");? 調用自定義構造函數
???????????? String c=a;調用拷貝構造函數,因為c一開始不存在,最好寫成String c(a);
??? A(void)??????????????????????????????????? //默認無參數構造函數
??? A(const A &a)???????????????????????? //默認復制構造函數
??? ~A(void);??????????????????????????????? //默認的析構函數
??? A & operator = (const A &a); //默認的賦值函數
既然能自動生成函數,為什么還需要自定義?原因之一是“默認的復制構造函數”和"默認的賦值函數“均采用”位拷貝“而非”值拷貝“
位拷貝? v.s.? 值拷貝
為便于說明,以自定義String類為例,先定義類,而不去實現。
復制代碼
#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;
};
復制代碼
位拷貝拷貝的是地址,而值拷貝拷貝的是內容。
如果定義兩個String對象a, b。當利用位拷貝時,a=b,其中的a.val=b.val;但是a.m_data=b.m_data就錯了:a.m_data和b.m_data指向同一個區域。這樣出現問題:
??? a.m_data原來的內存區域未釋放,造成內存泄露
??? a.m_data和b.m_data指向同一塊區域,任何一方改變,會影響到另一方
??? 當對象釋放時,b.m_data會釋放掉兩次
因此
當類中還有指針變量時,復制構造函數和賦值函數就隱含了錯誤。此時需要自己定義。
結論
??? 有一種特別常見的情況需要自己定義復制控制函數:類具有指針哈函數。
??? 賦值操作符和復制構造函數可以看成一個單元,當需要其中一個時,我們幾乎也肯定需要另一個
??? 三法則:如果類需要析構函數,則它也需要賦值操作符和復制構造函數
注意
??? 如果沒定義復制構造函數(別的不管),編譯器會自動生成默認復制構造函數
??? 如果定義了其他構造函數(包括復制構造函數),編譯器絕不會生成默認構造函數
??? 即使自己寫了析構函數,編譯器也會自動生成默認析構函數
因此此時如果寫String s是錯誤的,因為定義了其他構造函數,就不會自動生成無參默認構造函數。
復制構造函數? v.s.? 賦值函數
復制代碼
#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 << "自定義構造函數" << 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 << "自定義拷貝構造函數" << 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 << "自定義賦值函數" << 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 << "自定義析構函數" << 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;
}
復制代碼
執行結果
說明幾點
1. 賦值函數中,上來比較 this == &other 是很必要的,因為防止自復制,這是很危險的,因為下面有delete []m_data,如果提前把m_data給釋放了,指針已成野指針,再賦值就錯了
2. 賦值函數中,接著要釋放掉m_data,否則就沒機會了(下邊又有新指向了)
3. 拷貝構造函數是對象被創建時調用,賦值函數只能被已經存在了的對象調用
??? 注意:String a("hello"); String b("world");? 調用自定義構造函數
???????????? String c=a;調用拷貝構造函數,因為c一開始不存在,最好寫成String c(a);
總結
以上是生活随笔為你收集整理的复制构造函数 与 赋值函数 的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win7下U盘安装Ubuntu14.04
- 下一篇: 几步实现stm32上面移植mqtt