前面學習了構造函數的用法與初始化列表的特性,本章還是學習構造函數的內容。類型還能控制復制、賦值或撤銷該類型的對象,這時候,類通過特殊的成員函數:復制構造函數、賦值操作符和析構函數來控制這些行為。這些都屬於類對象的復制控制,挺重要的一部分內容。
一、本文內容的應用
1.復制構造函數的例子:
string str1("hello");
string str2(str1);
2.賦值操作符的例子:
string str1("hello");
string str2 = str1;
二、復制構造函數的應用於哪些地方
什麼樣的函數才是復制構造函數?
只有單個形參,而且該形參是對本類類型對象的引用(常用const修飾)。
復制構造函數用於:
1.用一個同類型的對象顯示或隱式初始化一個對象。
eg:
string str1("hello");
string str2(str1);
2.作為函數的實參
eg:
void Show(string str);
...
string str1("hello");
Show(str1); //存在一個復制操作,將str1對象隱式復制到Show函數的臨時對象
3.作為函數的返回值
eg:
string getString() const
{
string str("hello");
...
return str;
}
...
string str1 = getString(); //將getString函數的返回值的string對象復制到str1對象
4.初始化順序容器的元素
eg:
vector<string> vec(5); //聲明了一個vector,包含5個string對象;
//編譯器首先使用string默認構造函數創建一個臨時對象來初始化vec,然後使用復制構造函數將臨時值復制到vec的每個元素中。
5.根據元素初始化式列表初始化數組元素
這裡要強調的是:初始化式列表
a.每個元素調用一次默認構造函數
string str[10]; //調用了10次默認構造函數
b.每個元素調用一次復制構造函數
string str1, str2, str3, str4;
string str[] = { str1, str2, str3, str4};
三、合成的復制構造函數和合成的賦值操作符
如果用戶沒有顯示的定義復制構造函數或賦值操作符,編譯器將會默認的合成一個復制構造函數。復制構造函數與合成復制構造函數,或者賦值操作符與合成賦值操作符不同,編譯器默認合成的函數的行為是"逐個成員初始化",將新對象初始化為原對象的副本。逐個成員初始化不包括static成員,只負責初始化非static成員。
四、小試牛刀
這裡編寫一個最簡單的例子,來展示一下類對象的復制控制。
[cpp] #include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
CObj(const CObj &obj)
{
cout << "復制構造函數" << endl;
}
CObj & operator=(const CObj &obj)
{
if (&obj != this)
{
cout << "賦值操作符" << endl;
}
return *this;
}
};
int main()
{
CObj obj1;
cout << endl;
CObj obj2(obj1);
cout << endl;
CObj obj3;
obj3 = obj2;
return 0;
}
#include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
CObj(const CObj &obj)
{
cout << "復制構造函數" << endl;
}
CObj & operator=(const CObj &obj)
{
if (&obj != this)
{
cout << " www.2cto.com 賦值操作符" << endl;
}
return *this;
}
};
int main()
{
CObj obj1;
cout << endl;
CObj obj2(obj1);
cout << endl;
CObj obj3;
obj3 = obj2;
return 0;
}
執行結果:
[plain] 默認構造函數
復制構造函數
默認構造函數
賦值操作符
默認構造函數
復制構造函數
默認構造函數
賦值操作符
四、禁止復制
1.有些類需要完全禁止復制,怎麼辦?
只需將復制構造函數聲明為私有成員函數即可。
解釋:因為如果不聲明定義,編譯器將會默認合成一個復制構造函數。
例子:
[cpp] #include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
private:
CObj(const CObj &obj)
{
cout << "復制構造函數" << endl;
}
};
int main()
{
CObj obj1;
CObj obj2(obj1);
return 0;
}
#include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
private:
CObj(const CObj &obj)
{
cout << "復制構造函數" << endl;
}
};
int main()
{
CObj obj1;
CObj obj2(obj1);
return 0;
}
編譯報錯:
error: 'CObj::CObj(const CObj&)' is private
這就是禁止復制的效果,正是這裡想要的。
2.連友元函數和成員函數的復制也要禁止,怎麼辦?
如果想要連友元函數和成員函數的復制也禁止,那麼可以聲明一個私有的復制構造函數但不對其定義。
解釋:因為友元函數或成員函數可以訪問到類的私有成員,所以當然能調用私有的復制構造函數,所有將復制構造函數聲明為私有但不定義,就能避免友元或成員函數的調用。
例子:
[cpp] #include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
friend void fcopy();
private:
CObj(const CObj &obj);
};
void fcopy()
{
CObj obj1;
CObj obj2(obj1);
}
int main()
{
fcopy();
return 0;
}
#include <iostream>
using namespace std;
class CObj
{
public:
CObj()
{
cout << "默認構造函數" << endl;
}
friend void fcopy();
private:
CObj(const CObj &obj);
};
void fcopy()
{
CObj obj1;
CObj obj2(obj1);
}
int main()
{
fcopy();
return 0;
}
編譯報錯:
undefined reference to `CObj::CObj(CObj const&)'
這也正是這裡需要的結果,禁止復制成功,OK。
再接再厲!
摘自 gzshun的專欄