class Empty { };
編譯器會(huì)自動(dòng)聲明4個(gè)默認(rèn)函數(shù):構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù),析構(gòu)函數(shù)(當(dāng)然,如果不想使用自動(dòng)生成的函數(shù),就應(yīng)該明確拒絕),這些生成的函數(shù)都是public且inline。構(gòu)造函數(shù)對(duì)數(shù)據(jù)成員進(jìn)行初始化,使用未初始化值可能導(dǎo)致無法預(yù)知的錯(cuò)誤,所以,確保
每一個(gè)構(gòu)造函數(shù)都將
每一個(gè)成員初始化。
2)為什么構(gòu)造函數(shù)不能有返回值
如果有返回值,要么編譯器必須知道怎么處理返回值,要么就客戶程序員顯式調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),這樣,還有安全性么?
3)為什么構(gòu)造函數(shù)不能為虛函數(shù)
簡單來說,虛函數(shù)調(diào)用的機(jī)制,是知道接口而不知道其準(zhǔn)確對(duì)象類型的函數(shù),但是創(chuàng)建一個(gè)對(duì)象,必須知道對(duì)象的準(zhǔn)確類型;當(dāng)一個(gè)構(gòu)造函數(shù)被調(diào)用時(shí),它做的首要事情之一就是初始化它的VPTR來指向VTABLE。
4)構(gòu)造函數(shù)的一個(gè)面試題:
#include <iostream>
using namespace std;
class Base
{
private:
int i;
public:
Base(int x)
{
i = x;
}
};
class Derived : public Base
{
private:
int i;
public:
Derived(int x, int y)
{
i = x;
}
void print()
{
cout << i + Base::i << endl;
}
};
int main()
{
Derived A(2,3);
A.print();
return 0;
}
首先,是訪問權(quán)限問題,子類中直接訪問Base::i是不允許的,應(yīng)該將父類的改為protected或者public(最好用protected)
其次,統(tǒng)計(jì)父類和子類i的和,但是通過子類構(gòu)造函數(shù)沒有對(duì)父類變量進(jìn)行初始化;此處編譯會(huì)找不到構(gòu)造函數(shù),因?yàn)樽宇愓{(diào)用構(gòu)造函數(shù)會(huì)先找父類構(gòu)造函數(shù),但是沒有2個(gè)參數(shù)的,所以可以在初始化列表中調(diào)用父類構(gòu)造函數(shù)
最后個(gè)問題,是單參數(shù)的構(gòu)造函數(shù),可能存在隱式轉(zhuǎn)換的問題,因?yàn)閱螀?shù)構(gòu)造函數(shù),和拷貝構(gòu)造函數(shù)形式類似,調(diào)用時(shí)很可能會(huì)發(fā)生隱式轉(zhuǎn)換,應(yīng)加上explicit關(guān)鍵字,修改后如下
#include <iostream>
using namespace std;
class Base
{
protected:
int i;
public:
explicit Base(int x)
{
i = x;
}
};
class Derived : public Base
{
private:
int i;
public:
Derived(int x, int y):Base(x)
{
i = y;
}
void print()
{
cout << i + Base::i << endl;
}
};
int main()
{
Derived A(2,3);
A.print();
return 0;
}
2、初始化列表
1)使用初始化列表提高效率
常用的初始化可能如下:
class Student
{
public:
Student(string in_name, int in_age)
{
name = in_name;
age = in_age;
}
private :
string name;
int age;
};
這么寫,可以達(dá)到預(yù)期效果,不過不是最佳做法,因?yàn)樵跇?gòu)造函數(shù)中,是對(duì)name進(jìn)行賦值,
不是初始化,而string對(duì)象會(huì)先調(diào)用它的默認(rèn)構(gòu)造函數(shù),再調(diào)用string類(貌似是basic_string類)的賦值構(gòu)造函數(shù);對(duì)于上例的age,因?yàn)閕nt是內(nèi)置類型,應(yīng)該是賦值的時(shí)候獲得了初值。
要對(duì)成員進(jìn)行初始化,而不是賦值,可以采用初始化列表(member initialization list)改寫為如下:
class Student
{
public:
Student(string in_name, int in_age):name(in_name),age(in_age) {}
private :
string name;
int age;
};
結(jié)果與上例相同,不過在初始化的時(shí)候調(diào)用的是string的拷貝構(gòu)造函數(shù),而上例會(huì)調(diào)用兩次構(gòu)造函數(shù),從性能上會(huì)有不小提升
有的情況下,是必須使用初始化列表進(jìn)行初始化的:const對(duì)象、引用對(duì)象
2)初始化列表初始順序
考慮以下代碼:
#include <iostream>
using namespace std;
class Base
{
public:
Base(int i) : m_j(i), m_i(m_j) {}
Base() : m_j(0), m_i(m_j) {}
int get_i() const
{
return m_i;
}
int get_j() const
{
return m_j;
}
private:
int m_i;
int m_j;
};
int main()
{
Base obj(98);
cout << obj.get_i() << endl << obj.get_j() << endl;
return 0;
}
輸出為一個(gè)隨機(jī)數(shù)和98,為什么呢?因?yàn)閷?duì)于初始化列表而言,對(duì)成員變量的初始化,是嚴(yán)格按照聲明次序,而不是在初始化列表中的順序進(jìn)行初始化,如果改為賦值初始化則不會(huì)出現(xiàn)這個(gè)問題,當(dāng)然,為了使用初始化列表,還是嚴(yán)格注意聲明順序吧,比如先聲明數(shù)組大小,再聲明數(shù)組這樣。