多態
如果父類指針指向的是父類對象則調用父類中定義的函數
如果父類指針指向的是子類對象則調用子類中定義的重寫函數
先看看如果不進行多態的實驗
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
void print()
{
cout<<"Parent:print() do..."<<endl;
}
};
class Child : public Parent
{
public:
void print()
{
cout<<"Child:print() do..."<<endl;
}
};
int main()
{
Child child;
Parent *p = NULL;
p = &child;
p->print();
child.print();
system("pause");
return 0;
}
上面的例子,雖然把派生類的地址傳給了基類的指針,但是執行相同函數的話,還是執行了父類指針裏面的函數。
接下來看多態的實現
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void print() //那個函數需要進行多態的話,就得添加virtual
{
cout<<"Parent:print() do..."<<endl;
}
};
class Child : public Parent
{
public:
virtual void print()//那個函數需要進行多態的話,就得添加virtual
{
cout<<"Child:print() do..."<<endl;
}
};
int main()
{
Child child;
Parent parent;
Parent *p = NULL;
p = &child;
p->print();
p = &parent;
p->print();
system("pause");
return 0;
}
進行多態的三個條件:
1)繼承 2)虛函數 3)父類指針或者引用
多態的實現原理
當類中聲明虛函數時,編譯器會在類中生成一個虛函數表
虛函數表是一個存儲類成員函數指針的數據結構
虛函數表是由編譯器自動生成與維護的
virtual成員函數會被編譯器放入虛函數表中
當存在虛函數時,每個對象中都有一個指向虛函數表的指針(C++編譯器給父類對象、子類對象提前佈局vptr指針;當進行howToPrint(Parent *base)函數是,C++編譯器不需要區分子類對象或者父類對象,只需要再base指針中,找vptr指針即可。)
VPTR一般作爲類對象的第一個成員
虛析構函數
當用new申請一個派生類的地址賦值給父類指針,當這個變量出了定義域的範圍之後,是不會執行派生類的析構函數的,只有對析構函數定義成虛函數纔行。
例如:
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void print()
{
cout << "Parent:print() do..." << endl;
}
~Parent(){
printf("我是父類析構函數\n");
}
};
class Child : public Parent
{
public:
virtual void print()
{
cout << "Child:print() do..." << endl;
}
~Child(){
printf("我是子類析構函數\n");
}
};
int main()
{
Parent *p = new(Child);
delete p;
system("pause");
return 0;
}
加了virtual之後
#include <cstdlib>
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void print()
{
cout << "Parent:print() do..." << endl;
}
virtual ~Parent(){
printf("我是父類析構函數\n");
}
};
class Child : public Parent
{
public:
virtual void print()
{
cout << "Child:print() do..." << endl;
}
virtual ~Child(){
printf("我是子類析構函數\n");
}
};
int main()
{
Parent *p = new(Child);
delete p;
system("pause");
return 0;
}
先執行派生類的析構函數,再執行父類的析構函數,這就正常了