Virtual Function
[ Pure Virtual Function ] 纯虚函数:函数签名尾部附加 ' =0 ',无需实现。
[ Common Virtual Function ] 普通虚函数:函数签名附加缺省实现。
[ Abstract Class ] 抽象类:含纯虚函数的类即是抽象类,禁止实例化。
Specifier ' override ':
// Animal is an abstract class because of it has pure virtual functions.
class Animal
{
public:
// pure virtual function
virtual void Eat() = 0;
// pure virtual function
virtual void Drink() = 0;
// pure virtual function
virtual void Sleep() = 0;
// common virtual function - it has a default implmentation
virtual void Speek() { std::cout << "I'm an animal." << std::endl; }
};
// Abstract Class
// Mammal is still an abstract class because it does not override
// and implement all Animal's pure virtual functions.
class Mammal :public Animal
{
public:
virtual void Eat() override {};
virtual void BreastFeed() {};
};
Specifier ' final ':用于防止类被派生(derive)和防止虚函数被覆写(ovrride)
// AnasPlatyrhynchosLinnaeus can not have any Derived Classes because of 'final'.
class AnasPlatyrhynchosLinnaeus final :public Duck
{
public:
virtual void Eat() override {};
virtual void Drink() override {};
virtual void Sleep() override { std::cout << "I'm an Anas Platyrhynchos Linnaeus duck." << std::endl; };
// ...
};
class Chicken :public Animal
{
public:
// Chicken' derived classes can not override these virtual function because of 'final'.
virtual void Eat() override final {};
virtual void Drink() override final {};
// Chicken' derived classes can still override this virtual function.
virtual void Sleep() override {};
};
Virtual Function Table
Compiler在编译期为每个拥有虚函数的类添加一个成员:虚函数表指针(_vptr),4Bytes(32位平台),具体位置视Compiler而定。
通过new关键字,需要在堆上创建类实例(即对象),类实例内存中包括了一个_vptr和其他属性成员。
通过_vptr(虚函数表指针)可以找到虚函数表(该表所有的该类实例共享),该表实质是一个数组,数组中的元素即虚函数指针。
最后通过虚函数指针访问虚函数代码。
通过虚函数表实现运行时多态,与之相关的最关键部分:必须使用向上转型
C++规则:父类指针可以接收所有子类指针,而子类(没有更多的子类了)指针只能接受同样类型的指针。
这也是面向对象哲学的正确性。
这样就可以在运行时期,根据不同的运行时参数(同一父类的所有子类指针)来查询不同虚函数表,从而实现多态。
那么有一个更容易混淆的问题出现了,非虚函数是如何访问的?
目前看来可信的解释是,由于非虚函数在编译时期就已经可以确定调用,所以编译器在编译时将非虚函数的访问代码直接编译成该函数在内存中的位置。那么虚函数由于在编译时期还不能确定,但是可以根据运行时子类指针携带的_vptr找到虚函数表再确认调用的函数,那么可以看出来,函数调用的决定延后到了运行期。
实际上所有的动态都需要基于一定的静态,只是在更加动态的时候,静态的内容变得更少而已,这里的静态内容即每个子类所携带的信息,这些信息确实是在编译期确定的!
另vptr. vtable内存位置, refer http://www.tuicool.com/articles/iUB3Ebi
转自https://blog.csdn.net/primeprime/article/details/80776625
class Duck :public Animal
{
public:
virtual void Eat() override {};
virtual void Drink() override {};
virtual void Sleep() override { std::cout << "I'm a duck." << std::endl; };
// ...
};
// AnasPlatyrhynchosLinnaeus can not have any Derived Classes because of 'final'.
class AnasPlatyrhynchosLinnaeus final :public Duck
{
public:
virtual void Eat() override {};
virtual void Drink() override {};
virtual void Sleep() override { std::cout << "I'm an Anas Platyrhynchos Linnaeus duck." << std::endl; };
// ...
};
int main()
{
Animal* duck = new Duck();
Animal* anasPlatyrhynchosLinnaeusDuck1 = new AnasPlatyrhynchosLinnaeus();
Duck* anasPlatyrhynchosLinnaeusDuck2 = new AnasPlatyrhynchosLinnaeus();
duck->Sleep();
anasPlatyrhynchosLinnaeusDuck1->Sleep();
anasPlatyrhynchosLinnaeusDuck2->Sleep();
delete duck;
delete anasPlatyrhynchosLinnaeusDuck1;
delete anasPlatyrhynchosLinnaeusDuck2;
return 0;
}
Output:
I'm a duck.
I'm an Anas Platyrhynchos Linnaeus duck.
I'm an Anas Platyrhynchos Linnaeus duck.