在C++的子類中,定義某成員函數(shù)時(shí),我們通常需要顯式的調(diào)用其基類的版本。例如在一個(gè)繪圖類結(jié)構(gòu)中,子類只需要去繪制在子類添加進(jìn)去的部分圖形,然后再調(diào)用基類去完成基礎(chǔ)的圖形。這個(gè)成員函數(shù)有一般都是虛函數(shù)。對于構(gòu)造函數(shù),在子類的構(gòu)造函數(shù)中也可能顯式地執(zhí)行基類的構(gòu)造函數(shù)。
先看看一個(gè)例子,基類Shape的默認(rèn)構(gòu)造函數(shù)不分配name空間,但子類Line的默認(rèn)構(gòu)造函數(shù)會(huì)按照規(guī)則自動(dòng)產(chǎn)生name,這里我們假設(shè)name是private的,如果name不是private,問題會(huì)很簡單,也不會(huì)出現(xiàn)下述問題了。但在現(xiàn)實(shí)中,通常在基類的構(gòu)造函數(shù)會(huì)初使化一些重要的private成員,或者構(gòu)造函數(shù)比較長,在子類中不想復(fù)制這些代碼而希望直接調(diào)用到基類的構(gòu)造函數(shù)。一般我們可以在初使化式中直接構(gòu)造基類,但有的時(shí)候,需要先計(jì)算出基類構(gòu)造函數(shù)的參數(shù),如同本例中一樣需要先產(chǎn)生一個(gè)autoName。
class Shape{
public:
Shape(LPCTSTR name){
this->name = new TCHAR[lstrlen(name) + 1];
lstrcpy(this->name, name);
}
Shape(){
name = NULL;
}
virtual void draw() = 0;
LPCTSTR getName() const{ return name;};
private:
LPTSTR name;
};
class Line:public Shape{
public:
static int autoIdx;
Line(){
LPTSTR autoName = new TCHAR[32];
memset(autoName, 0, 32 * sizeof(TCHAR));
lstrcpy(autoName, L"NewLine");
_itow_s(autoIdx++, autoName + lstrlen(autoName), 8, 10);
this->Shape::Shape(autoName);
}
Line(LPCTSTR name):Shape(name){
}
void draw(){
}
};
int Line::autoIdx = 1;
int _tmain(int argc, _TCHAR* argv[])
{
Shape* l = new Line();
l->draw();
}
運(yùn)行該程序,按理來說應(yīng)該是沒有什么問題,但實(shí)際出現(xiàn)的錯(cuò)誤還是讓人丈二摸不著頭腦。

一開始我一直以為是不是draw()方法與基類的名稱不一致,反復(fù)的拷貝,比較參數(shù)與返回值,但一直彈出上面的錯(cuò)誤。因?yàn)槊髅髟贚ine類定義了draw()方法,怎么會(huì)調(diào)用得到Shape的純虛函數(shù)呢?折騰了近兩個(gè)小時(shí),沒轍了,只好注釋代碼一行一行地排查,最后發(fā)現(xiàn),當(dāng)將
this->Shape::Shape(autoName);
Examda提示: 注掉之后,這個(gè)問題便不存在了??磥韱栴}就一直出在上面的這行代碼上去了,仔細(xì)想想,想起虛函數(shù)表就是在構(gòu)造函數(shù)中初使化的,之所以一直調(diào)用基類的方法,一定就是在調(diào)用上面基類的構(gòu)造函數(shù)時(shí),之前已經(jīng)初使化好的虛函數(shù)指針被基類的虛函數(shù)指針覆蓋了。
其實(shí)之所以做上面的調(diào)用,還是受到Java語言的影響,在Java中,無論在什么位置,直接用super()就搞定了。在C++中,虛函數(shù)成員這樣調(diào)用沒有問題,但在構(gòu)造函數(shù)中,還是會(huì)出現(xiàn)比較嚴(yán)重的運(yùn)行故障,如果要在C++編碼中不要再出現(xiàn)類似的問題,還是需要透徹了解在C++的構(gòu)造函數(shù)到底干了些什么,以及虛函數(shù)表,這個(gè)隱藏在背后的實(shí)現(xiàn)虛函數(shù)功能的機(jī)制。
先看看一個(gè)例子,基類Shape的默認(rèn)構(gòu)造函數(shù)不分配name空間,但子類Line的默認(rèn)構(gòu)造函數(shù)會(huì)按照規(guī)則自動(dòng)產(chǎn)生name,這里我們假設(shè)name是private的,如果name不是private,問題會(huì)很簡單,也不會(huì)出現(xiàn)下述問題了。但在現(xiàn)實(shí)中,通常在基類的構(gòu)造函數(shù)會(huì)初使化一些重要的private成員,或者構(gòu)造函數(shù)比較長,在子類中不想復(fù)制這些代碼而希望直接調(diào)用到基類的構(gòu)造函數(shù)。一般我們可以在初使化式中直接構(gòu)造基類,但有的時(shí)候,需要先計(jì)算出基類構(gòu)造函數(shù)的參數(shù),如同本例中一樣需要先產(chǎn)生一個(gè)autoName。
class Shape{
public:
Shape(LPCTSTR name){
this->name = new TCHAR[lstrlen(name) + 1];
lstrcpy(this->name, name);
}
Shape(){
name = NULL;
}
virtual void draw() = 0;
LPCTSTR getName() const{ return name;};
private:
LPTSTR name;
};
class Line:public Shape{
public:
static int autoIdx;
Line(){
LPTSTR autoName = new TCHAR[32];
memset(autoName, 0, 32 * sizeof(TCHAR));
lstrcpy(autoName, L"NewLine");
_itow_s(autoIdx++, autoName + lstrlen(autoName), 8, 10);
this->Shape::Shape(autoName);
}
Line(LPCTSTR name):Shape(name){
}
void draw(){
}
};
int Line::autoIdx = 1;
int _tmain(int argc, _TCHAR* argv[])
{
Shape* l = new Line();
l->draw();
}
運(yùn)行該程序,按理來說應(yīng)該是沒有什么問題,但實(shí)際出現(xiàn)的錯(cuò)誤還是讓人丈二摸不著頭腦。

一開始我一直以為是不是draw()方法與基類的名稱不一致,反復(fù)的拷貝,比較參數(shù)與返回值,但一直彈出上面的錯(cuò)誤。因?yàn)槊髅髟贚ine類定義了draw()方法,怎么會(huì)調(diào)用得到Shape的純虛函數(shù)呢?折騰了近兩個(gè)小時(shí),沒轍了,只好注釋代碼一行一行地排查,最后發(fā)現(xiàn),當(dāng)將
this->Shape::Shape(autoName);
Examda提示: 注掉之后,這個(gè)問題便不存在了??磥韱栴}就一直出在上面的這行代碼上去了,仔細(xì)想想,想起虛函數(shù)表就是在構(gòu)造函數(shù)中初使化的,之所以一直調(diào)用基類的方法,一定就是在調(diào)用上面基類的構(gòu)造函數(shù)時(shí),之前已經(jīng)初使化好的虛函數(shù)指針被基類的虛函數(shù)指針覆蓋了。
其實(shí)之所以做上面的調(diào)用,還是受到Java語言的影響,在Java中,無論在什么位置,直接用super()就搞定了。在C++中,虛函數(shù)成員這樣調(diào)用沒有問題,但在構(gòu)造函數(shù)中,還是會(huì)出現(xiàn)比較嚴(yán)重的運(yùn)行故障,如果要在C++編碼中不要再出現(xiàn)類似的問題,還是需要透徹了解在C++的構(gòu)造函數(shù)到底干了些什么,以及虛函數(shù)表,這個(gè)隱藏在背后的實(shí)現(xiàn)虛函數(shù)功能的機(jī)制。