從零開始學習c++之拷貝構造函數

拷貝構造函數:

定義:

   如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則此構造函數是拷貝構造函數。

典型的拷貝構造函數:

class Foo{
public:
    Foo(){};       //默認構造函數
    Foo(const Foo& f){};      //默認拷貝構造函數
}

拷貝構造函數的第一個參數必須是引用類型,雖然可以定義一個接收非const引用的拷貝構造函數,但此參數幾乎總是一個const的引用。


何時使用拷貝構造函數:

(1)拷貝初始化(用=定義變量)

(2)將一個對象作爲實參傳遞給非引用類型的形參

(3)一個返回類型爲非引用類型的函數返回一個對象


示例題目:

假如Point是一個類類型,它有一個public的拷貝構造函數,指出下面程序片段中哪些地方使用了拷貝構造函數;

Point global;
Point func(Point p)
{
    Point local = p,*heap = new Point(global);
    *heap = local;
    Point pa[4] = {local,*heap};
    return *heap;
}
解答:

(1)local = p,將p拷貝給local

(2)*heap = local,將local拷貝給heap指定的地址

(3)Point pa[4] = {local,*heap};    ,將local和*heap拷貝給數組的前兩位元素

(4)return *heap;

示例代碼:

#include <iostream>
#include <string>
using namespace std;

static int objectCount = 0;  //全局變量存儲對象數量
class HowMany {
public :
	HowMany() { objectCount++; print("HowMany()"); }      //默認構造函數,並輸出
	void print(const string& msg = "") {
		if (msg.size() != 0) cout << msg << ": ";     //輸出string類型參數
		cout << "ObjectCount:" << objectCount << endl;    //輸出全局變量
	}
	~HowMany() {                        //析構函數
		objectCount--;
		print("~HowMany()");      
	}
};


HowMany f(HowMany x) {
	cout << "Begin of f " << endl;
	x.print("x argument inside of f()");
	cout << "End of f" << endl;
	return x;
}

int main()
{
	HowMany h;
	h.print("After construction of h");
	HowMany h2 = f(h);
	h.print("After call of f()");
	

    return 0;
}
輸出結果:

HowMany() : ObjectCount:1                                      //h構造 count++
After construction of h : ObjectCount:1                     //h調用print函數,count == 1
Begin of f
x argument inside of f() : ObjectCount : 1                 //參數x沒有調用構造函數,count未變
End of f
~HowMany() : ObjectCount : 0                                  //f函數結束後,卻析構了x,count--
After call of f() : ObjectCount : 0
~HowMany() : ObjectCount : -1                                 //h析構
~HowMany() : ObjectCount : -2                                  //h2析構

可以發現:f函數在調用HowMany類型參數x時,沒有顯性的調用構造函數,所以objectCount沒有++,但卻析構了x,objectCount--,導致最終結果的Count並沒有歸零


現在修改示例代碼的HowMany類:

class HowMany {
public :
	HowMany() { objectCount++; print("HowMany()"); }      
	HowMany(const HowMany& h) { objectCount++; print("HowMany(h)"); }          //新的拷貝構造函數
	void print(const string& msg = "") {
		if (msg.size() != 0) cout << msg << ": ";     
		cout << "ObjectCount:" << objectCount << endl;    
	}
	~HowMany() {              //析構函數
		objectCount--;
		print("~HowMany()");      
	}
};

運行後結果:

HowMany(): ObjectCount:1                       //h調用構造
After construction of h: ObjectCount:1        
HowMany(h): ObjectCount:2                       //f函數參數x調用構造
Begin of f
x argument inside of f(): ObjectCount:2
End of f
HowMany(h): ObjectCount:3                         //h2調用構造
~HowMany(): ObjectCount:2
After call of f(): ObjectCount:2
~HowMany(): ObjectCount:1
~HowMany(): ObjectCount:0
此時,h,h2,和參數x分別調用了我們所寫構造函數,增加了count,並在最後析構了自身。


在C++中,如果沒有寫默認的拷貝構造函數,編譯器會自動調用默認的拷貝構造函數,默認的拷貝構造函數會作成員變量的拷貝,拷貝對應的類型來構造,並遞歸的進行下去。所以如果構造函數的參數不是引用類型,此時構造函數會無限循環,所以拷貝構造函數的第一個參數必須是引用類型。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章