1、基類的構造函數不能為虛函數,因為對象不完整
2、不要在構造函數和析構函數裡面調用虛函數,可能會發生未定義的行為
構造派生類對象時,首先調用基類構造函數初始化對象的基類部分。在執行基類構造函數時,對象的派生類部分是未初始化的。實際上,此時的對象還不是一個派生類對象。
析構派生類對象時,首先撤銷/析構他的派生類部分,然後按照與構造順序的逆序撤銷他的基類部分。
是以,在運作構造函數或者析構函數時,對象都是不完整的。為了适應這種不完整,編譯器将對象的類型視為在調用構造/析構函數時發生了變換,即:視對象的類型為目前構造函數/析構函數所在的類的類類型。由此造成的結果是:在基類構造函數或者析構函數中,會将派生類對象當做基類類型對象對待。
而這樣一個結果,會對構造函數、析構函數調用期間調用的虛函數類型的動态綁定對象産生影響,最終的結果是:如果在構造函數或者析構函數中調用虛函數,運作的都将是為構造函數或者析構函數自身類類型定義的虛函數版本。 無論有構造函數、析構函數直接還是間接調用虛函數。
3、最好把基類的析構函數聲明為虛函數
那麼在什麼情況下,析構函數要定義為虛函數呢?
看一個例子:
class Person
{
public:
virtual Person* BuyTickets()
{
cout << "買票" << endl;
return this;
}
~Person()
{
cout << "~Person" << endl;
}
};
class Student :public Person
{
public:
virtual Student* BuyTickets()
{
cout << "買票——半價" << endl;
return this;
}
~Student()
{
cout << "~Student" << endl;
}
protected:
int _num;
};
void Fun(Person& p)
{
p.BuyTickets();
}
void test()
{
Person* ptr = new Student;
delete ptr;
}
int main()
{
test();
system("pause");
return 0;
}
運作結果為:
可知隻調用了基類的析構函數,改為虛函數為:
class Person
{
public:
virtual Person* BuyTickets()
{
cout << "買票" << endl;
return this;
}
virtual ~Person()
{
cout << "~Person" << endl;
}
};
class Student :public Person
{
public:
virtual Student* BuyTickets()
{
cout << "買票——半價" << endl;
return this;
}
~Student()
{
cout << "~Student" << endl;
}
protected:
int _num;
};
void Fun(Person& p)
{
p.BuyTickets();
}
void test()
{
Person* ptr = new Student;
delete ptr;
}
int main()
{
test();
system("pause");
return 0;
}