900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > C++多态 虚函数作用及底层实现原理

C++多态 虚函数作用及底层实现原理

时间:2021-03-28 05:51:28

相关推荐

C++多态 虚函数作用及底层实现原理

简述C++虚函数作用及底层实现原理

1.foreword

C++是面向对象程序设计,其包括3项特点:

(1)数据抽象:接口和实现分离

(2)继承:父类和子类

(3)多态:动态绑定

本文讨论多态。

当父类希望子类重新定义某些函数时,用virtual关键字声明为虚函数。

当我们使用一个基类类型的引用或者指针,调用一个虚函数时就引发动态绑定/多态的发生。函数运行版本由传入的实参类型决定。可以用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

父类指针好像有“多种类型”。这是一种泛型技术,试图用不变的代码实现可变的算法,比如模板技术,虚函数技术等。

引用或者指针的静态类型与动态类型不同这一事实,是C++支持多态的核心。

上述这句话选自《C++ Primer》,颇有玄门正宗之感。复杂的技术根植于底层的原理,所以搞清原理方可深入浅出。

当我们使用基类的引用、指针去调用基类中定义的一个虚函数时,并不知道该函数真正作用的对象是什么类型,可能是一个基类对象,也可能是一个派生类对象,这一切直到运行时才可知道。所以多态又称运行时绑定/动态绑定。

对于非虚函数的调用在编译时即可确定,哪个对象,哪个方法,地址是确定的。

2.虚函数底层实现原理

当你记住上面的概念时,它仅是概念。但当真正了解底层原理后,发现其中妙不可言。

如果让你来实现虚函数的功能?你会怎么实现?

复杂的代码?高深的原理?其实C++中实现的非常接地气。

虚函数是通过虚函数表和虚函数指针来实现的。

该表一般位于某类型的对象实例在内存中的最开始的位置。

单类继承

class Base {public:virtual void f() { cout << "Base::f()" << endl; }virtual void g() { cout << "Base::g()" << endl; }virtual void h() { cout << "Base::h()" << endl; }};

父类对象其在内存中布局示意如下:

虚函数表的尾部为虚函数表的结束结点。

再定义一个子类,此时并不覆盖父类的虚函数:

class Derived :public Base {public:virtual void f1() { cout << "Derived::f1()" << endl; }virtual void g1() { cout << "Derived::g1()" << endl; }virtual void h1() { cout << "Derived::g1()" << endl; }};

此时可以得知:

(1)虚函数按照声明顺序放在表中;

(2)父类的虚函数,排在子类虚函数之前。

当我们把子类中的函数覆盖时:

class Derived :public Base {public:// f() overridevoid f() { cout << "Derived::f1()" << endl; }virtual void g1() { cout << "Derived::g1()" << endl; }virtual void h1() { cout << "Derived::g1()" << endl; }};

此时可以得知:

(1)子类覆盖的虚函数,放在原来父类该虚函数的位置;所以当父类指针指向该子类对象时,调用方法就会调用子类重载的方法;

(2)没有被覆盖的虚函数依旧。

多类继承

当发生多类继承时:

虚函数表内存排列示意如下:

此时可以得知:

(1)每个继承的父类,都有自己的虚函数表;

(2)子类虚函数,放在第一个声明顺序的父类的表中。

当子类虚函数重写时,此时类的继承为:

子类将f( )函数重写,则内存中的排列为:

此时我们可以得知:

(1)三个父类虚函数表中的f( )函数被替换为子类函数指针。因此当我们用任一静态类型的父类指针来指向子类时,调用f( )时就调用的子类的f( )。

参考资料:

[1] /webary/p/4731457.html

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。