首先,多态是什么?
摘抄自 多态(维基百科):
—— 多态(英语:polymorphism),是指计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作称之。在cplusclpus中有这么一句话:
One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature.
即,在C++中指向子类的指针和指向基类的指针是兼容的。
所以,在C++中,我们可以通过指针指向不同的对象来实现多态(引用和指针类似,虽然有些差别,但是这里不展开讨论)
虚函数机制
参考《Effective C++》条款07中对虚表指针以及虚函数表的讲解,这里阐述下虚函数机制的原理:
—- 在C++中,若在类中定义了虚函数,则编译器会为改类生成一个虚函数表,这个表中保存了该类以及父类所有的虚函数,并在实例化该类时,会在类对象中添加一个虚表指针,这个指针就只向该虚表指针,当使用指针或引用调用函数时,C++会根据对象中保存的虚表指针查找需要调用的函数,具体的细节下面慢慢分析。
class Fa
{
int Fa_a;
public:
void foo()
{
cout << "父类的foo函数 \n";
}
};
class childA:public Fa
{
int CA_a;
public:
void foo()
{
cout << "子类A的foo函数 \n";
}
};
- 在没有定义虚函数时,内存布局中只存在父类和子类的数据成员对象
class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};
class childA:public Fa
{
int CA_a;
public:
void foo()
{
cout << "子类A的foo函数 \n";
}
};
- 在添加了虚函数后,内存布局中增加了一个vfptr,并且生成了一个vftable,分别是虚表指针和虚函数表。
class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};
class childA:public Fa
{
int CA_a;
};
class Fa
{
int Fa_a;
};
class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}
};
在这两个例子中需要注意两点:
- 当在父类中先定义虚函数时,vfptr出现在父类下面,当子类先定义虚函数时,vfptr出现在子类下面
- 当在子类中定义了和父类中虚函数相同的函数时,虚函数表中保存的是子类的虚函数,当子类中没有定义与父类虚函数相同的函数时,虚函数表中保存的是父类的虚函数。
—- 这里不难得出一下两点结论:
- 子类会继承父类的虚函数表,并且当子类定义了与父类相同的函数(返回值,函数签名都相同)时,子类的虚函数会覆盖父类的虚函数保存到虚函数表中。
- 当子类出现父类中没有的虚函数时,子类的虚函数会被添加到虚函数表中,并且偏移量大于任意父类的虚函数。
— 上面的例子还不是很清晰的说明了第二点,下面在放一个清晰一点的例子:
class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
virtual void FFF()
{
cout << "";
}
};
class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}
virtual void funcA()
{
cout << "子类A的func函数\n";
}
};
这个例子可以让我们清楚的理解上面两点结论,父类中先定义了虚函数,于是vfptr在Fa下面,子类定义了与父类相同的函数,于是子类的虚函数覆盖了父类的虚函数,子类新增了虚函数,于是虚函数表中添加了该虚函数,并且在父类虚函数的后面。
理解了原理后我们就可以使用虚函数来实现多态了:
#include<iostream>
using namespace std;
class Fa
{
int Fa_a;
public:
virtual void foo()
{
cout << "父类的foo函数 \n";
}
};
class childA:public Fa
{
int CA_a;
public:
virtual void foo()
{
cout << "子类A的foo函数 \n";
}
};
class childB :public Fa
{
int CB_a;
public:
virtual void foo()
{
cout << "子类B的foo函数\n";
}
};
void func()
{
Fa * ff = new Fa();
ff->foo();
ff =new childA();
ff->foo();
ff = new childB();
ff->foo();
}
int main()
{
func();
system("pause");
return 0;
}
- 可以看到,我们使用了同一个ff指针,却调用了三个不同的函数,这就是多态了。