多态
如果父类指针指向的是父类对象则调用父类中定义的函数
如果父类指针指向的是子类对象则调用子类中定义的重写函数
先看看如果不进行多态的实验
#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;
}
先执行派生类的析构函数,再执行父类的析构函数,这就正常了