简介
继承与虚函数与多态是浑然一体的三个概念,父类中虚函数可以表现出多态特性,具体是将子类的地址传递给指向父类的指针(类型向上转换),通过这样的指针调用虚函数依然会调用到子类中的这个虚函数的实现,这就是多态。
实现虚函数多态的技术原理
C++编译器在每个包含虚函数类的对象中隐匿生成了一个指针vptr,这个指针指向一张虚函数表,虚函数表中记录着每个虚函数距离当前对象起始地址的偏移量。当调用虚函数时,通过这个指针找到虚函数距离当前对象的偏移量确定此函数,此时如果传递的时子对象地址,自然会调用子函数中的虚函数实现体。
对象切边
当通过指针传递地址时可以实现多态,但是如果是传值的话,就不会出现多态,出现的是子类对象被强制转换为父类对象,丢失子类对象多余成员。
析构函数和构造函数中的虚函数
析构函数和构造函数中的虚函数调用都会本地函数,这样做的一个原因是,当构造函数没有完成时,这个类对象隐匿的vptr指针还没有构建完成,另外这样实现也有很多好处。
使用继承的类的析构函数应该使用虚函数修饰符
析构函数使用虚函数修饰符会保证多态时,正确的调用析构函数,具体可见代码实现。
#include<iostream>
#include<string>
using namespace std;
class noVirtual
{
public:
string name;
string getName() {
name = "nV";
return name;
};
};
class BasePet
{
public:
string name;
virtual string getName() {
cout << "this is BasePet" << endl;
name = "basePet";
return name;
};
~BasePet() {
cout << "~BasePet" << endl;
}
};
class Dog :public BasePet
{
public:
virtual float getSpeed() {
return 1.0;
};
string getName() {
cout << "this is dog" << endl;
name = "dog";
return name;
};
};
class People
{
public:
People()
{
getName();
}
virtual void getName()
{
cout << "this is in people()" << endl;
}
~People() {
cout << "this is in ~people()" << endl;
}
};
class Man:public People
{
public:
Man()
{
getName();
}
void getName()
{
cout << "this is in Man()" << endl;
}
virtual ~Man() {
cout << "this is in ~Man()" << endl;
}
};
class Son :public Man
{
public:
Son()
{
getName();
}
void getName()
{
cout << "this is in Son()" << endl;
}
~Son() {
cout << "this is in ~Son()" << endl;
}
};
int main ()
{
/*
//确定虚函数确实多了一个虚指针
noVirtual nV;
BasePet bP;
Dog dG;
cout << "size of nV " << sizeof(nV) << endl;
cout << "size of bP " << sizeof(bP) << endl;
cout << "size of dG " << sizeof(dG) << endl;
int* p;
cout << "size of 一个指针大小 " << sizeof(p) << endl;
//使用虚函数机制 多态
BasePet* bPtr = new Dog();
bPtr->getName();
delete bPtr;
//对象切片,当不使用地址传递时,虚函数机制失效,子类对象强制转化为父类对象,调用父类函数
BasePet bPO;
Dog dGO;
bPO = dGO;
bPO.getName();
//构造函数和虚构函数中调用析构函数都会调用本地版本的函数
Son s;
*/
//析构函数应该设计成虚函数,否则会有内存泄露的风险
People* pp = new Son();
delete pp; //直接调用了people的析构函数
Man* m = new Son();
delete m; //从son的析构函数开始调用
}