zoukankan      html  css  js  c++  java
  • C++vptr初始化时间

    给出如下代码段:

    [cpp] view plaincopy
     
    1. #include <iostream>  
    2. #include "stdio.h"  
    3. using namespace std;  
    4.   
    5. class A  
    6. {  
    7. public:  
    8.     A(int arg):m_a(arg)  
    9.     {  
    10.         cout << "constructor of A" << endl;  
    11.         output();  
    12.     }  
    13.     virtual void output()  
    14.     {   
    15.         cout << "output A" << endl;  
    16.     }  
    17.     virtual void display()  
    18.     {   
    19.         output();   
    20.     }  
    21. private:  
    22.     int m_a;  
    23. };  
    24.   
    25. class B : public A  
    26. {  
    27. public:  
    28.     B(int arg1, int arg2):A(arg1), m_b(arg2)  
    29.     {   
    30.         cout << "constructor of B" << endl;  
    31.     }  
    32.     virtual void output()  
    33.     {   
    34.         cout << "output B" << endl;  
    35.     }  
    36. private:  
    37.     int m_b;  
    38. };  
    39.   
    40.   
    41. int main(int argc, char* argv[])  
    42. {  
    43.     B b(1, 2);  
    44.     b.display();  
    45.     return 0;  
    46. }  

    这段代码会输出什么?

    其实这个问题本质是在A的构造函数及display函数中分别调用的是A类的output函数还是B类的output函数。

    输出结果是:

    constructor of A
    output A
    constructor of B
    output B

    说明A的构造函数中调用的是A类的output函数,display函数中调用的是B类的output函数。

    为什么会这样呢?这跟指向虚函数表的指针(vptr)初始化时间有关。

    《Inside the c++ Object model》中指出vptr初始化的时间为:

             After invocation of the base class constructors but before execution of user-provided code 

    or the expansion of members initialized within the member initialization list.

    意思是在所有基类构造函数之后,但又在自身构造函数或初始化列表之前。

    vptr初始化是在初始化列表之前还是之后是跟编译器实现有关的,在VC++6.0编译器中是在初始化列表之后,从上面这段代码的部分汇编码就可以看出。

    B类的构造函数的汇编码:

    00401250   push        ebp
    00401251   mov         ebp,esp
    00401253   sub         esp,44h
    00401256   push        ebx
    00401257   push        esi
    00401258   push        edi
    00401259   push        ecx
    0040125A   lea         edi,[ebp-44h]
    0040125D   mov         ecx,11h
    00401262   mov         eax,0CCCCCCCCh
    00401267   rep stos    dword ptr [edi]
    00401269   pop         ecx
    0040126A   mov         dword ptr [ebp-4],ecx
    0040126D   mov         eax,dword ptr [ebp+8]
    00401270   push        eax
    00401271   mov         ecx,dword ptr [ebp-4]
    00401274   call        @ILT+15(A::A) (00401014)                                               //调用基类构造函数
    00401279   mov         ecx,dword ptr [ebp-4]
    0040127C   mov         edx,dword ptr [ebp+0Ch]
    0040127F   mov         dword ptr [ecx+8],edx
    00401282   mov         eax,dword ptr [ebp-4]                                                        //这里初始化m_b
    00401285   mov         dword ptr [eax],offset B::`vftable' (0042f030)                //初始化vptr
    30:           cout << "constructor of B" << endl;
    0040128B   push        offset @ILT+50(std::endl) (00401037)
    00401290   push        offset string "constructor of B" (0042f01c)
    00401295   push        offset std::cout (00433ea0)
    0040129A   call        @ILT+180(std::operator<<) (004010b9)
    0040129F   add         esp,8
    004012A2   mov         ecx,eax
    004012A4   call        @ILT+135(std::basic_ostream<char,std::char_traits<char> >::operator<<) (0040108c)

    A类构造函数汇编码:

    004012E0   push        ebp
    004012E1   mov         ebp,esp
    004012E3   sub         esp,44h
    004012E6   push        ebx
    004012E7   push        esi
    004012E8   push        edi
    004012E9   push        ecx
    004012EA   lea         edi,[ebp-44h]
    004012ED   mov         ecx,11h
    004012F2   mov         eax,0CCCCCCCCh
    004012F7   rep stos    dword ptr [edi]
    004012F9   pop         ecx
    004012FA   mov         dword ptr [ebp-4],ecx
    004012FD   mov         eax,dword ptr [ebp-4]
    00401300   mov         ecx,dword ptr [ebp+8]
    00401303   mov         dword ptr [eax+4],ecx
    00401306   mov         edx,dword ptr [ebp-4]                                         //初始化m_a
    00401309   mov         dword ptr [edx],offset A::`vftable' (0042f050)  //初始化vptr
    10:           cout << "constructor of A" << endl;
    0040130F   push        offset @ILT+50(std::endl) (00401037)
    00401314   push        offset string "constructor of A" (0042f03c)
    00401319   push        offset std::cout (00433ea0)
    0040131E   call        @ILT+180(std::operator<<) (004010b9)
    00401323   add         esp,8
    00401326   mov         ecx,eax
    00401328   call        @ILT+135(std::basic_ostream<char,std::char_traits<char> >::operator<<) (0040108c)
    11:           output();
    0040132D   mov         ecx,dword ptr [ebp-4]
    00401330   call        @ILT+195(A::output) (004010c8)

    这里还可以看出b对象的vptr是被初始化了两次:

    先在基类的构造函数前初始化为指向基类虚函数表(vtble)的指针,然后在自身构造函数前初始化为指向自身类vtble的指针。

    而且不管哪种情况,vptr都是在自身构造函数体之前初始化。

    所以,在A类的构造函数中调用output函数时,b对象尚未构造完成,vptr指向A类的vtble,当然调用A类的output函数。

    当执行b.display时,b对象已经构造完成,vptr指向B类的vtble,当然调用B类的display函数。

    Ok,这就解释了前面的输出结果。

    接着再引用一个《Inside the c++ object model》中的问题:

    Is it safe to invoke a virtual function of the class within its constructor's member initialization list?

    我相信聪明的你已经知道答案。

    http://blog.csdn.net/passion_wu128/article/details/8580306

  • 相关阅读:
    服务器基础知识
    web服务器与应用服务器
    QGraphics
    对话框Dialog
    信号和槽
    nginx 杂记
    go语言nsq源码解读八 http.go、http_server.go
    go语言nsq源码解读七 lookup_protocol_v1.go
    go语言nsq源码解读六 tcp.go、tcp_server.go
    go语言nsq源码解读五 nsqlookupd源码registration_db.go
  • 原文地址:https://www.cnblogs.com/findumars/p/5006174.html
Copyright © 2011-2022 走看看