第一篇:拷貝構(gòu)造函數(shù)剖析
拷貝構(gòu)造函數(shù)剖析
在講課過程中,我發(fā)現(xiàn)大部分學(xué)生對(duì)拷貝構(gòu)造函數(shù)的理解不夠深入,不明白自定義拷貝構(gòu)造函數(shù)的必要性。因此,我將這部分內(nèi)容,進(jìn)行了總結(jié)。
拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),其形參為本類的對(duì)象引用。功能:使用一個(gè)已經(jīng)存在的對(duì)象始初化同類的一個(gè)新對(duì)象。這樣得到對(duì)象和原來的對(duì)象具有完全相同的數(shù)據(jù)成員,即相同的屬性。
拷貝構(gòu)造函數(shù)的函數(shù)原型:
A(const A& other){ … … }
拷貝構(gòu)造函數(shù)的應(yīng)用場(chǎng)合:
當(dāng)用類的一個(gè)對(duì)象去初始化該類的另一個(gè)對(duì)象時(shí);若函數(shù)的形參為類對(duì)象,調(diào)用函數(shù)時(shí),實(shí)參賦值給形參;當(dāng)函數(shù)的返回值是類對(duì)象時(shí)。比如:
A a1(10);
A a2 = a1;
A a3(a1);// 構(gòu)造函數(shù) // 拷貝構(gòu)造函數(shù) // 拷貝構(gòu)造函數(shù)
默認(rèn)拷貝構(gòu)造函數(shù):成員變量之間的“值”拷貝
編寫拷貝構(gòu)造函數(shù)的必要性
class A
{
public:
A(const char* data)
{
name = new char[strlen(data)+ 1];
strcpy(name, data);
}
A(const A& other)
{
name = new char[strlen(other.name)+ 1];
strcpy(name, other.name);
}
private:
char* name;
};
考察:char* data = “abcd”;A a1(data);A a2 = a1;
如果未定義拷貝構(gòu)造函數(shù),會(huì)有何種后果?
現(xiàn)將a1賦給a2,缺省拷貝構(gòu)造函數(shù)的“位拷貝”意味著執(zhí)行a2.name = a1.name。這將造成二個(gè)錯(cuò)誤:一是a2.name和a1.name指向同一塊內(nèi)存,任何一方變動(dòng)都會(huì)影響另一方;二是在對(duì)象被析構(gòu)時(shí),name被釋放了兩次。
第二篇:拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別
拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別
一個(gè)類會(huì)默認(rèn)生成它的string()//默認(rèn)普通構(gòu)造函數(shù)
void string(const string &a)//默認(rèn)拷貝構(gòu)造函數(shù),如果自己不實(shí)現(xiàn),會(huì)用這個(gè)默認(rèn)的//采用“位拷貝”的方式,對(duì)有成員指針的情況,一定有
//問 題, 因?yàn)椤拔豢截悺?,指向同一地址空間,自己//實(shí)現(xiàn),改成“值拷貝”
~stirng()//默認(rèn)析構(gòu)函數(shù)
const string& operation=(const string &a)//默認(rèn)賦值函數(shù),如果自己不實(shí)現(xiàn),//默認(rèn)的也是采用“位拷貝”的方式
“位拷貝”,string a(b);
除了a,b對(duì)象的地址不一樣,但a,b成員對(duì)象都指向的同一地址空間。如果delete a, 就會(huì)刪掉b的內(nèi)容,所以位拷貝,對(duì)有指針成員變量的類,非常危險(xiǎn)
拷貝構(gòu)造函數(shù)發(fā)生的例子:
Aa(1);//調(diào)用構(gòu)造函數(shù),Ab(a);//調(diào)用拷貝構(gòu)造函數(shù)
Ac=a;//第一次賦值,因?yàn)閷?duì)象還沒初始化,還是調(diào)用拷貝構(gòu)造函數(shù),c=b;//已經(jīng)初始化的對(duì)象才能調(diào)用賦值函數(shù)
voidf(Aa);//函數(shù)聲明
f(c);//實(shí)參傳遞時(shí)調(diào)用拷貝構(gòu)造函數(shù),但是編譯器會(huì)根據(jù)具體情況把這個(gè)過程優(yōu)化掉
總結(jié):
1.拷貝構(gòu)造函數(shù)只有在定義一個(gè)新的類對(duì)象并且用已有的對(duì)象進(jìn)行初始化時(shí)調(diào)用.2.賦值函數(shù)只有在已經(jīng)初始化(對(duì)象已定義)的情況下被調(diào)用
例如有一個(gè)類叫做‘myclass',并有一個(gè)實(shí)例:b
那么,myclassa=b;//拷貝構(gòu)造函數(shù)
myclassa;
a=b;//賦值
兩者調(diào)用的時(shí)間不一樣,第一種情況下,a此時(shí)還沒有被分配空間,在扶植的同時(shí)還要生成資源;
第二種情況下,a一構(gòu)造完成,已經(jīng)有了資源,所以此時(shí)等號(hào)只進(jìn)行賦值。
第三篇:不要輕視拷貝構(gòu)造函數(shù)與賦值函數(shù)
不要輕視拷貝構(gòu)造函數(shù)與賦值函數(shù)
由于并非所有的對(duì)象都會(huì)使用拷貝構(gòu)造函數(shù)和賦值函數(shù),程序員可能對(duì)這兩個(gè)函數(shù)有些輕視。請(qǐng)先記住以下的警告,在閱讀正文時(shí)就會(huì)多心:
? 本章開頭講過,如果不主動(dòng)編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),編譯器將以“位拷貝”的方式自動(dòng)生成缺省的函數(shù)。倘若類中含有指針變量,那么這兩個(gè)缺省的函數(shù)就隱含了錯(cuò)誤。以類String的兩個(gè)對(duì)象a,b為例,假設(shè)a.m_data的內(nèi)容為“hello”,b.m_data的內(nèi)容為“world”。
現(xiàn)將a賦給b,缺省賦值函數(shù)的“位拷貝”意味著執(zhí)行b.m_data = a.m_data。這將造成三個(gè)錯(cuò)誤:一是b.m_data原有的內(nèi)存沒被釋放,造成內(nèi)存泄露;二是b.m_data和a.m_data指向同一塊內(nèi)存,a或b任何一方變動(dòng)都會(huì)影響另一方;三是在對(duì)象被析構(gòu)時(shí),m_data被釋放了兩次。
? 拷貝構(gòu)造函數(shù)和賦值函數(shù)非常容易混淆,常導(dǎo)致錯(cuò)寫 錯(cuò)用??截悩?gòu)造函數(shù)是在對(duì)象被創(chuàng)建時(shí)調(diào)用的,而賦值函數(shù)只能被已經(jīng)存在了的對(duì)象調(diào)用。以下程序中,第三個(gè)語句和第四個(gè)語句很相似,你分得清楚哪個(gè)調(diào)用了拷貝構(gòu)造函數(shù),哪個(gè)調(diào)用了賦值函數(shù)嗎? Stringa(“hello”);
Stringb(“world”);
Stringc = a;// 調(diào)用了拷貝構(gòu)造函數(shù),最好寫成 c(a);
c = b;// 調(diào)用了賦值函數(shù)
本例中第三個(gè)語句的風(fēng)格較差,宜改寫成String c(a)以區(qū)別于第四個(gè)語句。
第四篇:拷貝構(gòu)造函數(shù)的參數(shù)類型必須是引用
拷貝構(gòu)造函數(shù)的參數(shù)類型必須是引用
在C++中,構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),析構(gòu)函數(shù)和賦值函數(shù)(賦值運(yùn)算符重載)是最基本不過的需要掌握的知識(shí)。但是如果我問你“拷貝構(gòu)造函數(shù)的參數(shù)為什么必須使用引用類型?”這個(gè)問題,你會(huì)怎么回答? 或許你會(huì)回答為了減少一次內(nèi)存拷貝? 很慚愧的是,我的第一感覺也是這么回答。不過還好,我思索一下以后,發(fā)現(xiàn)這個(gè)答案是不對(duì)的。
原因:
如果拷貝構(gòu)造函數(shù)中的參數(shù)不是一個(gè)引用,即形如CClass(const CClass c_class),那么就相當(dāng)于采用了傳值的方式(pass-by-value),而傳值的方式會(huì)調(diào)用該類的拷貝構(gòu)造函數(shù),從而造成無窮遞歸地調(diào)用拷貝構(gòu)造函數(shù)。因此拷貝構(gòu)造函數(shù)的參數(shù)必須是一個(gè)引用。
需要澄清的是,傳指針其實(shí)也是傳值,如果上面的拷貝構(gòu)造函數(shù)寫成CClass(const CClass* c_class),也是不行的。事實(shí)上,只有傳引用不是傳值外,其他所有的傳遞方式都是傳值。
先從一個(gè)小例子開始:(自己測(cè)試一下自己看看這個(gè)程序的輸出是什么?)
[cpp] view plain copy #include
using namespace std;
class CExample
{
private:
int m_nTest;
public:
CExample(int x): m_nTest(x)
//帶參數(shù)構(gòu)造函數(shù)
{
cout << “constructor with argument”< } // 拷貝構(gòu)造函數(shù),參數(shù)中的const不是嚴(yán)格必須的,但引用符號(hào)是必須的 CExample(const CExample & ex) //拷貝構(gòu)造函數(shù) { m_nTest = ex.m_nTest; cout << “copy constructor”< } CExample& operator =(const CExample &ex) //賦值函數(shù)(賦值運(yùn)算符重載) { cout << “assignment operator”< m_nTest = ex.m_nTest; return *this; } void myTestFunc(CExample ex) { } }; int main(void) { CExample aaa(2); CExample bbb(3); bbb = aaa; CExample ccc = aaa; bbb.myTestFunc(aaa); return 0; } 這個(gè)例子的輸出結(jié)果是: [cpp] view plain copy constructor with argument // CExample aaa(2); constructor with argument // CExample bbb(3); assignment operator // bbb = aaa; copy constructor // CExample ccc = aaa; copy constructor // bbb.myTestFunc(aaa); 如果你能一眼看出就是這個(gè)結(jié)果的話,恭喜你,可以站起來扭扭屁股,不用再往下看了。 如果你的結(jié)果和輸出結(jié)果有誤差,那拜托你謙虛的看完。 第一個(gè)輸出: constructor with argument // CExample aaa(2); 如果你不理解的話,找個(gè)人把你拖出去痛打一頓,然后嘴里還喊著“我是二師兄,我是二師兄.......” 第二個(gè)輸出:constructor with argument // CExample bbb(3); 分析同第一個(gè) 第三個(gè)輸出: assignment operator // bbb = aaa; 第四個(gè)輸出: copy constructor // CExample ccc = aaa; 這兩個(gè)得放到一塊說??隙〞?huì)有人問為什么兩個(gè)不一致。原因是,bbb對(duì)象已經(jīng)實(shí)例化了,不需要構(gòu)造,此時(shí)只是將aaa賦值給bbb,只會(huì)調(diào)用賦值函數(shù),就這么簡(jiǎn)單,還不懂的話,撞墻去!但是ccc還沒有實(shí)例化,因此調(diào)用的是拷貝構(gòu)造函數(shù),構(gòu)造出ccc,而不是賦值函數(shù),還不懂的話,我撞墻去! 第五個(gè)輸出: copy constructor // bbb.myTestFunc(aaa); 實(shí)際上是aaa作為參數(shù)傳遞給bbb.myTestFunc(CExample ex),即CExample ex = aaa;和第四個(gè)一致的,所以還是拷貝構(gòu)造函數(shù),而不是賦值函數(shù),如果仍然不懂,我的頭剛才已經(jīng)流血了,不要再讓我撞了,你就自己使勁的再裝一次吧。 通過這個(gè)例子,我們來分析一下為什么拷貝構(gòu)造函數(shù)的參數(shù)只能使用引用類型。 看第四個(gè)輸出: copy constructor // CExample ccc = aaa; 構(gòu)造ccc,實(shí)質(zhì)上是ccc.CExample(aaa);我們假如拷貝構(gòu)造函數(shù)參數(shù)不是引用類型的話,那么將使得 ccc.CExample(aaa)變成aaa傳值給ccc.CExample(CExample ex),即CExample ex = aaa,因?yàn)?ex 沒有被初始化,所以 CExample ex = aaa 繼續(xù)調(diào)用拷貝構(gòu)造函數(shù),接下來的是構(gòu)造ex,也就是 ex.CExample(aaa),必然又會(huì)有aaa傳給CExample(CExample ex), 即 CExample ex = aaa;那么又會(huì)觸發(fā)拷貝構(gòu)造函數(shù),就這下永遠(yuǎn)的遞歸下去。 所以繞了那么大的彎子,就是想說明拷貝構(gòu)造函數(shù)的參數(shù)使用引用類型不是為了減少一次內(nèi)存拷貝,而是避免拷貝構(gòu)造函數(shù)無限制的遞歸下去。 附帶說明,在下面幾種情況下會(huì)調(diào)用拷貝構(gòu)造函數(shù): a、顯式或隱式地用同類型的一個(gè)對(duì)象來初始化另外一個(gè)對(duì)象。如上例中,用對(duì)象c初始化d; b、作為實(shí)參(argument)傳遞給一個(gè)函數(shù)。如CClass(const CClass c_class)中,就會(huì)調(diào)用CClass的拷貝構(gòu)造函數(shù); c、在函數(shù)體內(nèi)返回一個(gè)對(duì)象時(shí),也會(huì)調(diào)用返回值類型的拷貝構(gòu)造函數(shù); d、初始化序列容器中的元素時(shí)。比如 vector e、用列表的方式初始化數(shù)組元素時(shí)。string a[] = {string(“hello”), string(“world”)};會(huì)調(diào)用string的拷貝構(gòu)造函數(shù)。 如果在沒有顯式聲明構(gòu)造函數(shù)的情況下,編譯器都會(huì)為一個(gè)類合成一個(gè)缺省的構(gòu)造函數(shù)。如果在一個(gè)類中聲明了一個(gè)構(gòu)造函數(shù),那么就會(huì)阻止編譯器為該類合成缺省的構(gòu)造函數(shù)。和構(gòu)造函數(shù)不同的是,即便定義了其他構(gòu)造函數(shù)(但沒有定義拷貝構(gòu)造函數(shù)),編譯器總是會(huì)為我們合成一個(gè)拷貝構(gòu)造函數(shù)。 另外函數(shù)的返回值是不是引用也有很大的區(qū)別,返回的不是引用的時(shí)候,只是一個(gè)簡(jiǎn)單的對(duì)象,此時(shí)需要調(diào)用拷貝構(gòu)造函數(shù),否則,如果是引用的話就不需要調(diào)用拷貝構(gòu)造函數(shù)。[cpp] view plain copy #include using namespace std; class A { private: int m_nTest; public: A() { } A(const A& other) //構(gòu)造函數(shù)重載 { m_nTest = other.m_nTest; cout << “copy constructor”< } A & operator =(const A& other) { if(this!= &other) { m_nTest = other.m_nTest; cout<<“Copy Assign”< } return *this; } }; A fun(A &x) { return x; //返回的不是引用的時(shí)候,需要調(diào)用拷貝構(gòu)造函數(shù) } int main(void) { A test; fun(test); system(“pause”); return 0; } 分享一道筆試題目,編譯運(yùn)行下圖中的C++代碼,結(jié)果是什么?(A)編譯錯(cuò)誤;(B)編譯成功,運(yùn)行時(shí)程序崩潰;(C)編譯運(yùn)行正常,輸出10。請(qǐng)選擇正確答案并分析原因。 [cpp] view plain copy class A { private: int value; public: A(int n) { value = n; } A(A other) { value = other.value; } void Print() { cout< } }; int main(void) { A a = 10; A b = a; b.Print(); return 0; } 答案:編譯錯(cuò)誤。在復(fù)制構(gòu)造函數(shù)中傳入的參數(shù)是A的一個(gè)實(shí)例。由于是傳值,把形參拷貝到實(shí)參會(huì)調(diào)用復(fù)制構(gòu)造函數(shù)。因此如果允許復(fù)制構(gòu)造函數(shù)傳值,那么會(huì)形成永無休止的遞歸并造成棧溢出。因此C++的標(biāo)準(zhǔn)不允許復(fù)制構(gòu)造函數(shù)傳值參數(shù),而必須是傳引用或者常量引用。在Visual Studio和GCC中,都將編譯出錯(cuò)。 構(gòu)造函數(shù) 1.設(shè) f(x),g(x)分別為定義在R上的奇函數(shù)和偶函數(shù),當(dāng)x?0時(shí),f?(x)g(x)?f(x)g?(x)?0,且g(?3)?0,則不等式f(x)g(x)?0的解集為______.2.設(shè)f(x)是定義在R上的奇函數(shù),且f(2)?0,當(dāng)x?0時(shí),有x? f?(x)?f(x)?0 恒成立,則不等式x2f(x)?0的解集為__________.3.已知函數(shù)f(x)是定義在R上的奇函數(shù),且當(dāng)x?(??,0)時(shí),有x?<0成立,若a?30.3? b f?(x)+f(x)1 3f(3 0.3),b??log?3?? f(log ? 3),c?(log 9)?f(log 9),則a、、c的大小關(guān)系為__________.f(x),則當(dāng)a?0 4.已知可導(dǎo)函數(shù)f(x)滿足f?(x)?系為__________.時(shí),f(a)與ea? f(0)的大小關(guān) 5.若函數(shù)f(x)對(duì)任意的x?R都有f?(x)? A.3f(ln2)?2f(ln3) f(x) 成立,則__________.B.3f(ln2)?2f(ln3) C.3f(ln2)?2f(ln3)D.3f(ln2)與2f(ln3)的大小關(guān)系不確定 6.設(shè)f(x)是R上的奇函數(shù),且f(?1)?0,當(dāng)x?0時(shí),(x2 ?1)?f?(x)?2x?f(x)?0,則不等式f(x)?0的解集為__________.7.已知函數(shù)f(x)是定義在(0,??)的非負(fù)可導(dǎo)函數(shù),且滿足x?對(duì)任意正數(shù)a、b,若a f?(x)+f(x)?0,B.af(b)?bf(a)C.af(a)?f(b) D.bf(b)?f(a),8.已知f(x)與g(x)都是定義在R上的函數(shù),g(x)?0,f?(x)g(x)? f(x)?a?g(x),x f(x)g?(x)?0 f(1)g(1) ? f(?1)g(?1) ? .在有窮數(shù)列? ?f(n)? ?(n?1,2,?,10)中,前kg(n)?? 項(xiàng)和 為 1516,則k=__________.第五篇:構(gòu)造函數(shù)