構造函數
首先說什麼是構造函數。構造一個對象,就必須有一個構造函數供它初始化時調用,如果你沒有定義構造函數,則編輯器會自動生成一個預設構造函數。如果構造的對象有參數,則初始化時會調用對應的有參數的構造函數,否則就會調用無參數的構造函數,是以一般會寫一個無參數的構造函數供别人修改程式使用。如果自己已經定義一個構造函數,則編輯器不會再自己生成預設構造函數。
例如:
class point
{
public:
point(int x, int y);//聲明一個有參數的構造函數
point();//聲明一個無參數的構造函數
private :
int x;
int y;
};
point::point(int x, int y){} //有參數的構造函數
point::point(){}//無參數的構造函數
int main()
{
point a; //沒有參數的對象
point d(2,3);//帶參數的對象
return 0;
}
這個時候編輯不會報錯,但是如果你去掉上面任何一個構造函數,編譯就會報錯。
需要注意的是:
1 構造函數不能規定傳回類型,不能有return語句。
2 構造函數的名稱必須和類的名稱一緻
3 當你已經構造了一個或者幾個包含參數的構造函數(參數沒有預設值),最好在寫一個無參數的構造函數,友善别人調試。
如果一個構造函數有全部的參數,且參數全部由預設值,那麼就相當于是一個預設構造函數。即不可同時定義一個無參構造函數和一個參數全部都有預設值的構造函數
例如把上面的
point::point(int x, int y){} //有參數的構造函數
修改成
point::point(int x=0, int y=0) {}
這時候編譯就會報錯,這時候這個有參數的構造函數就是相當于一個預設構造函數。
複制構造函數
當你構造了一個對象,并且想用已有的對象對此對象初始化,這個時候就需要調用複制構造函數。
調用複制構造函數有三種情況:
1,定義一個對象,用本類的另外一個對象作為初始值時;
2,函數的形參是類的對象,調用函數時将使用實參對象初始化形參對象,
3,函數傳回值是類的對象時,函數執行完傳回主調函數時,将使用return語句中的對象初始化一個無名對象,傳遞給主調函數,此時發生複制構造。
需要注意的是複制構造函數的參數必須使用引用傳參,使用傳值方式會引發無窮遞歸調用。為什麼呢?
#include "stdafx.h"
#include <iostream>
#include <cstdlib>
using namespace std;
class point
{
public:
point();//無參數的構造函數
point(const point &p);
int getx();
private :
int x;
int y;
};
point::point(const point &p)
{
x = p.x;
y = p.y;
cout << "calling the copy constructor" << endl;
}
point::point() { x = 0; y = 0; }
void fun1(point p)
{
cout << p.getx() << endl;
}
point fun2()
{
point a;
return a;
}
int point :: getx()
{
return x;
}
int main()
{
point a;
point b(a);
cout << b.getx() << endl;
fun1(b);
b = fun2();
cout << b.getx() << endl;
return 0;
}
來看
point b(a);
這個是調用複制構造函數的第一種情況。
point::point(const point &p)
{
x = p.x;
y = p.y;
cout << "calling the copy constructor" << endl;
}
如果這裡的&p,不是引用,而是一個常值傳遞p,那麼就相當于是p=a;又因為a是初始化過的對象,而p是沒有初始化的對象,是以還是要繼續調用複制構造函數,這樣就會無窮無盡的調用下去。是以複制構造函數的參數使用引用類型不是為了減少一次記憶體拷貝, 而是避免拷貝構造函數無限制的遞歸下去。
上面的例子運作結果為:
calling the copy constructor
0
calling the copy constructor
0
calling the copy constructor
0
這裡三次調用複制構造函數,分别對應上面的三種情況!