double-patching 通过两次虚函数实现在VS上测试(More effective C++233)

来源:互联网 发布:剑网3霸刀捏脸数据 编辑:程序博客网 时间:2024/06/07 03:28
class A;class B;class Vbase{public:virtual void Test(Vbase & obj)=0;virtual void Test(A &obj)=0;virtual void Test(B &obj)=0;};class A:public Vbase{public:virtual void Test(Vbase & aVbase){aVbase.Test(*this);cout<<"second dispatch"<<endl;}virtual void Test(A &aA){cout<<"A + A called"<<endl;}virtual void Test(B &aB){cout<<"B + A called"<<endl;}};class B:public Vbase{public:virtual void Test(Vbase & aVbase){aVbase.Test(*this);cout<<"second dispatch"<<endl;}virtual void Test(A &aA){cout<<"A + B called"<<endl;}virtual void Test(B &aB){cout<<"B + B called"<<endl;}};

class Vbase{public:virtual void Test(Vbase &){cout<<"base\n";}};class B;class A:public Vbase{public:virtual void Test(Vbase & aVbase){aVbase.Test(*this);cout<<"second dipatch"<<endl;}virtual void Test(A &aA){cout<<"A + A called"<<endl;}virtual void Test(B &aB){cout<<"A + B called"<<endl;}};class B:public Vbase{public:virtual void Test(Vbase & aVbase){aVbase.Test(*this);cout<<"second dipatch"<<endl;}virtual void Test(A &aA){cout<<"B + A called"<<endl;}virtual void Test(B &aB){cout<<"B + B called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Vbase *p1 = new A();Vbase *p2 = new B();p1->Test(*p2);cout<<"second phase"<<endl;p2->Test(*p1);return 0;}


一直不是按照静态类型选择重载版本,调用陷入死递归直到栈耗尽

完全按照书本信息,在基类里将每一个声明为纯虚函数,果然就OK了,看来声明一个函数是虚函数必须要从基类开始(这个基类通常是静态类型的入口),并不是在所有派生类中将函数声明为虚函数就OK的,因为此时基类函数没有相应的虚表项,此时它将直接进行静态的链接?


后来关闭基类虚接口定义中的最后一个,果然在第二个测试结果执行异常,因为执行的是静态绑定,多分发一次得到错误的调用结果,而第一个测试用例正确,看来还是没有从整个流程层面清晰地认识虚函数调用机制,并不掌握执行动态绑定的时机,并不是相应子类的函数都声明为虚函数的时候编译器就会执行动态绑定,编译器是通过静态类型是基类的入口进入的,当检查基类没有相应的虚表项就执行了静态绑定;


后续:来个最简单的测试结束(纠正平时很容易犯的一个严重问题,动态要从也只需从基类声明virtual,如果基类没有声明相应借口,绑定时的入口信息编译器会报错,没有定义基类的相应符号,如果声明了接口而没有加virtual关键字,一个严重的隐患就此种下了,编译器会发现其没有虚表项直接进行了静态绑定)

class A;class B;class Vbase{public:void Test(){cout<<"Based called"<<endl;}};class A:public Vbase{public:virtual void Test(){cout<<"A called"<<endl;}};class B:public Vbase{public:virtual void Test()                      //每个分支都有但入口没有 没用{cout<<"B called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Vbase *p1 = new A();p1->Test();}


输出是 

Based called  不是我们想要的吧,以后要小心了

原创粉丝点击