C++的多态如何在编译和运行期实现
C++的多态如何在编译和运行期实现
多态是什么?简单来说,就是某段程序调用了一个API接口,但是这个API有许多种实现,根据上下文的不同,调用这段API的程序,会调用该API的不同实现。今天我们只关注继承关系下的多态。
还是得通过一个例子来看看C++是怎样在编译期和运行期来实现多态的。很简单,定义了一个Father类,它有一个testVFunc虚函数哟。再定义了一个继承Father的Child类,它重新实现了testVFunc函数,当然,它也学习Father定义了普通的成员函数testFunc。大家猜猜程序的输出是什么?
[cpp]- #include <iostream>
- using namespace std;
- class Father
- {
- public:
- int m_fMember;
- void testFunc(){
- cout<<"Father testFunc "<<m_fMember<<endl;
- }
- virtual void testVFunc(){
- cout<<"Father testVFunc "<<m_fMember<<endl;
- }
- Father(){m_fMember=1;}
- };
- class Child : public Father{
- public:
- int m_cMember;
- Child(){m_cMember=2;}
- virtual void testVFunc(){cout<<"Child testVFunc "<<m_cMember<<":"<<m_fMember<<endl;}
- void testFunc(){cout<<"Child testFunc "<<m_cMember<<":"<<m_fMember<<endl;}
- void testNFunc(){cout<<"Child testNFunc "<<m_cMember<<":"<<m_fMember<<endl;}
- };
- int main()
- {
- Father* pRealFather = new Father();
- Child* pFalseChild = (Child*)pRealFather;
- Father* pFalseFather = new Child();
- pFalseFather->testFunc();
- pFalseFather->testVFunc();
- pFalseChild->testFunc();
- pFalseChild->testVFunc();
- pFalseChild->testNFunc();
- return 0;
- }
- Father testFunc 1
- Child testVFunc 2:1
- Child testFunc 0:1
- Father testVFunc 1
- Child testNFunc 0:1
首先让我们用gcc -S来生成汇编代码,看看main函数里是怎么调用这五个test*Func方法的。
[cpp]- movl $16, %edi
- call _Znwm
- movq %rax, %rbx
- movq %rbx, %rdi
- call _ZN6FatherC1Ev
- movq %rbx, -32(%rbp)
- movq -32(%rbp), %rax
- movq %rax, -24(%rbp)
- movl $16, %edi
- call _Znwm
- movq %rax, %rbx
- movq %rbx, %rdi
- call _ZN5ChildC1Ev
- movq %rbx, -16(%rbp)
- movq -16(%rbp), %rdi
- <span style="color:#ff0000;"> call _ZN6Father8testFuncEv 本行对应pFalseFather->testFunc();</span>
- movq -16(%rbp), %rax
- movq (%rax), %rax
- movq (%rax), %rax
- movq -16(%rbp), %rdi
- <span style="color:#ff0000;"> call *%rax 本行对应pFalseFather->testVFunc();</span>
- movq -24(%rbp), %rdi
- <span style="color:#ff0000;"> call _ZN5Child8testFuncEv 本行对应pFalseChild->testFunc();</span>
- movq -24(%rbp), %rax
- movq (%rax), %rax
- movq (%rax), %rax
- movq -24(%rbp), %rdi
- <span style="color:#ff0000;"> call *%rax 本行对应pFalseChild->testVFunc(); </span>
- movq -24(%rbp), %rdi
- <span style="color:#ff0000;"> call _ZN5Child9testNFuncEv 本行对应pFalseChild->testNFunc(); </span>
- movl $0, %eax
- addq $40, %rsp
- popq %rbx
- leave
红色的代码,就是在依次调用上面5个test*Func。可以看到,第1、3次testFunc调用,其结果已经在编译出来的汇编语言中定死了,C++代码都是调用某个对象指针指向的testFunc()函数,输出结果却不同,第1次是:Father testFunc 1,第3次是:Child testFunc 0:1,原因何在?在编译出的汇编语言很明显,第一次调用的是_ZN6Father8testFuncEv代码段,第三次调用的是_ZN5Child8testFuncEv代码段,两个不同的代码段!编译完就已经决定出同一个API用哪种实现,这就是编译期的多态。
|
评论暂时关闭