C++中構造函數是特殊的成員函數,只要定義類的新對象就會執行構造函數,構造函數是保證每個對象的數據成員具有合適的初始值。
構造函數特點:
1)構造函數與類同名,不能指定返回類型,且不能聲明爲const函數,詳見你所不知道的 const
2)構造函數可以重載,所以可以有多個構造函數
sales_item(void) /*const*/;
sales_item(istream &is /*= cin */);
sales_item(const string & book ); //must const
sales_item(const sales_item &org);
3)調用構造函數,使用不同的實參就會調用不同的構造函數,如下
sales_item sales0;
sales_item sales1("hello world");
sales_item("hello world"); //也可以
sales_item sales2 = sales_item("123456"); //定義
sales_item sales3(cin);
構造函數的初始化式:
函數後面可以包含一個初始化列表,原型爲形參後緊跟一個冒號,然後就是以逗號分開的數據成員列表,初始化值放在圓括號內,如下
sales_item::sales_item(void)
: isbn(1,'0'), units_sold(0), revenue(0.0), a(0), b(a){}
初始化注意之處:
1)構造函數分兩階段進行,初始化階段和普通的計算階段(計算階段就是函數體中的語句或者稱爲賦值階段)
2)未提供顯式初始化的變量使用與初始化變量相同的規則進行初始化,內置或者複合類型依賴於對象的作用域,局部不初始化全局初始化爲0,類類型使用默認的構造函數
3)必須提供初始化列表的情況,如下
screen::screen(void)//contents由string默認構造函數初始化
: nConstVar(0)/*contents("")*/,cursor(0),height(0),width(0),access_ctr(0){}
const int nConstVar; //必須在直接初始化列表進行初始化,如果在函數體中就會出錯,還有就是引用的初始化,如下sales_item::sales_item(void)
: isbn(1,'0'), units_sold(0), revenue(0.0), a(0), b(a){}
int &b; b一定要顯式初始化
4)成員初始化的次序
class x {
int i;
int j;
public:
x(int val):j(val), i(j){};
};
這裏我在vs2012編譯下通過了,顯式初始化次序對這個沒有影響,在gcc version 4.4.1使用g++編譯同樣通過。但是在vs2012調試後發現,i=-858993460,j=1 也就是說其實i並沒有提供的j的值初始化,所以最好嚴格按照初始化順序初始化,儘可能避免使用成員來初始化,最好統一使用形參來進行初始化。5)類類型的初始化可以使用該類型的任意構造函數,也可以使用默認的構造函數。
6)類成員的顯示初始化,直接初始化成員也是可以的,但是必須滿足一下幾點
a. 類的全體成員必須是public
b. 沒有定義構造函數
如下:
struct init
{
int ival;
char * ptr;
};
init initval = {1, NULL};
默認實參:
1)可以爲構造函數提供默認的實參,但是這個形參後面的所有形參必須提供默認值,所以需要合理調整形參的位置。
2)另外默認值只能提供在類聲明的位置處,不能在定義處提供,否則編譯會出錯。如下
class y
{
public:
y(ifstream &ifs, const string &str = ""):strVal(str), nVal(0), dVal(NULL), ifsVal(ifs){};
~y(){};
private:
const string strVal;
int nVal;
double* dVal;
ifstream& ifsVal;
};
3)可以有默認值,但是提供默認值後有些需要合併的函數就需要刪除,不能出現調用時出現歧義,如下
sales_item(void) /*const*/;
~sales_item(void);
sales_item(istream &is = cin );
sales_item(const string & book = "" ); //must const
這裏就會發現,如果調用sale_item默認不帶形參的構造函數就會出現歧義,不知該調用哪個
默認構造函數:
1)合成的默認構造函數,一個類哪怕只定義一個構造函數,編譯器就不會生成合成默認構造函數
2)合成的默認構造函數使用與變量初始化相同的規則來初始化成員,如果類包含內置類型或者複合類型的成員,則該類不應該依賴於合成的默認構造函數。
3)類通常應該定義一個默認構造函數,只要定義了其他構造函數,提供默認構造函數總是對的
4)使用默認構造函數時,切記不可寫成如下的形式
sales_item item();
這種寫法是錯誤的,因爲這裏其實聲明的item是函數,返回類型是sales_item,但是下面是允許的
sales_item item1 = sales_item(); //定義
或者
sales_item* item2 = new sales_item();
隱式轉轉:
當調用構造函數的時候,如果對形參沒有特別說明,就有可能發生隱式轉換,如下
const string book = "9-99-9999";
item.same_ishn(book);
這裏就將string轉換爲sales_item類類型,但是有時候卻需要抑制這種轉化,如下
explicit sales_item(void); // 防止隱式轉換