成员函数被重载的特征: ==================================================== 这几个概念都有一个共同点:函数名称相同,所以不免让人混淆,大致的区别如下: @H_502_64@ {public : virtual voID f( float x) {cout << " Base::f(folat) " << x << endl;} voID g( float x) {cout << " Base::g(float) " << x << endl;} } ; class Derived: public Base @H_502_64@ {public : virtual voID f( float x) {cout << " Derived::f(float) " << x << endl;} voID g( int x) {cout << " Deriver::g(int) " << x << endl;} } ; int main() @H_502_64@ {Derived d; Base * pb =& d; Derived * pd =& d; pb -> f( 3.14f ); pd -> f( 3.14f ); pb -> g( 3.14f ); // 输出结果:Base::g(float)3.14 pd -> g( 3.14f ); // 输出结果:DervIEd::g(int)3 return 0 ; } 在调用f函数的时候,派生类Derived的f函数覆盖了基类Base的f函数,而派生类Derived的g函数隐藏了基类Base的g函数. 为什么?理由很简单,f函数是virtual函数,但是g函数不是.我们可以把Base类和Derived类看成这样的一个struct: struct Base @H_502_64@ {voID (*g)(float); // Base类型的函数指针,不可变 struct Vtable *__vptr; // 虚拟函数指针数组,可变 } ; voID __Baseg( float ) @H_502_64@ {cout<<"Base::g(folat)"<<x<<endl; } struct Derived @H_502_64@ {voID (*g)(float); // Derived类型的函数指针,可变 } ; voID __Derivedg( float ) @H_502_64@ {cout<<"Deriver::g(int)"<<x<<endl; } struct Vtable @H_502_64@ {voID (*f)(float); // 函数指针 } ; voID __Basef( float ) @H_502_64@ {cout<<"Base::f(folat)"<<x<<endl; } voID __Derivedf( float ) @H_502_64@ {cout<<"Deriver::f(int)"<<x<<endl; } 在程序编译的时候,函数指针f就已经是确定的了,但是__vptr根据不同的而有分别,而这个变化是运行期动态决定的. 也就是说:f的地址不可变,__vptr可变. 回到上面的例子中,Base *pb=&d;的时候只是用Derived类对象d的__vptr修改了Base类pb的__vptr指针,但是当Base类成员建立的 时候f函数指针就是不能改变的. 当函数被声明为virtual的时候,就激活了多态机制,程序在运行的时候会根据类型的实际类型到Vtable中查找函数指针,因此对函数g的调用就是这样子的: pb->__vptr->g(); 而对f的调用就是一般的类成员函数指针的调用了:pb->f(),因为这个类型在程序编译的时候已经确认了,所以在程序运行的时候是不能发生改变的. 综上,可以把 Derived d; Base *pb=&d; 的过程分解为: d.g = __Derivedg; d.__vptr->f = __Derivedf; pb->g = __Baseg; // 这里根据指针的真正类型确定函数指针 pb->__vptr = d.__vptr; // 这里只是简单的指针赋值,因此访问到的就是Derived的函数了 最后在调用: pb->f(3.14f); pb->g(3.14f); 实际上是: pb->__vptr->__Derivedf(3.14f); __Baseg(3.14f); 这么写就明白最后在调用的时候为什么会用那样的结果了,可以看出多了一个__vptr这个间接层实现了所谓的"动态绑定". 最后,需要说明的一点是:实际上在c++中,非static和非virtual的函数指针并不会在一个class中保存它的函数指针,上面把函数g的指针写在struct里面只是为了方便说明这样的问题:在编译阶段这个函数就已经是确定的不可改变的了.特此说明一下. 覆盖 是指派生类函数覆盖基类函数,特征是:
|
以上是内存溢出为你收集整理的探索C++的秘密之:重载,覆盖,和隐藏全部内容,希望文章能够帮你解决探索C++的秘密之:重载,覆盖,和隐藏所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)