C++vptr初始化时间
给出如下代码段:
- #include <iostream>
- #include "stdio.h"
- using namespace std;
- class A
- {
- public:
- A(int arg):m_a(arg)
- {
- cout << "constructor of A" << endl;
- output();
- }
- virtual void output()
- {
- cout << "output A" << endl;
- }
- virtual void display()
- {
- output();
- }
- private:
- int m_a;
- };
- class B : public A
- {
- public:
- B(int arg1, int arg2):A(arg1), m_b(arg2)
- {
- cout << "constructor of B" << endl;
- }
- virtual void output()
- {
- cout << "output B" << endl;
- }
- private:
- int m_b;
- };
- int main(int argc, char* argv[])
- {
- B b(1, 2);
- b.display();
- return 0;
- }
这段代码会输出什么?
其实这个问题本质是在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
C++vptr初始化时间的更多相关文章
- 为什么要问Servlet的初始化时间
Servlet的init方法到底是在什么时候调用的? j2ee specification和java doc中有以下说明 如果load-on-startup设置为>=0, 部署的时候就会调用. ...
- Spark延长SparkContext初始化时间
有些应用中可能希望先在driver上运行一段java单机程序,然后再初始化SparkContext用集群模式操作java程序返回值.从而避免过早建立SparkContext对象分配集群资源,使资源长时 ...
- Unity-Animator深入系列---StateMachineBehaviour初始化时间测试
回到 Animator深入系列总目录 结果和想的有点出入 测试结果: 1.SMB初始化会被调用多次,次数不可控,当Animator组件重复开关则重复初始化. 2.SMB支持构造函数 MyClass p ...
- day03_12/13/2016_bean的管理之作用域与初始化时间
在Spring中,Bean有几种作用域: 1.singleton作用域 当一个bean的作用域设置为singleton,那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean ...
- 深入探索c++对象模型
第一章关于对象 c++在布局和存取时间的额外负担主要有virtual引起 virtual function:运行期动态绑定 virtual base class :base class多次出现在派生类 ...
- 【Android自学日记】使用DatePicker以及TimePicker显示当前日期和时间
DatePicker 1.获取一个日历对象: Calendar cal=Calendar.getInstance(); 2.获取当前日期及时间: int year=cal.get(Calendar.Y ...
- iOS时间问题
在iOS开发中,经常会遇到各种各样的时间问题,8小时时差,时间戳,求时间间隔,农历等等.解决办法网上比比皆是,但大多零零散散,很多资料并没有说明其中问题.这里集中总结一下,以便于以后查阅和供大家参考. ...
- C#----对时间结构DateTime的使用(时间日期的使用)
1.其初始化时间是0001--0--0 0:0:0 class Program { DateTime dt; static void Main(string[] args) { Program pp ...
- BW标准数据源初始化设置
在安装了一干补丁和做好了BW与R3的链接之后(此处有BISIS操心,具体事宜不详),我们就可以登录到R3系统看个究竟了. 磨刀不误砍柴工,先检查一下两边系统的补丁: R3端如下, ,貌似我们是19,通 ...
随机推荐
- Asp.net SignalR 初试和应用笔记一 认识和使用 SignalR
如果你在用QQ,微信.你会知道,广告和消息无处不在.也有好的一面,比如通过QQ或微信,微博等及时聊天功能,你找到了你的初恋,你找到了小学的班级等等. 这里的及时通信在很多应用场所能用到,比如: 1.球 ...
- 如何用EXCEL表计算今天是本年的第几周?
单元格内输入如下代码计算出来的数字即是当日在本年度的第几周,如下: =INT((TODAY()-DATE(YEAR(TODAY()),1,1)-WEEKDAY(DATE(YEAR(TODAY()),1 ...
- {A} + {B}(unique水)
{A} + {B} Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- jQuery插件jqplot的详细配置说明和渲染器
jQuery插件jqplot的详细配置说明和渲染器 (2012-08-23 08:57:42) 转载▼ 标签: jqplot 详细配置 渲染器 it 分类: 技术类 jQuery.jqplot插件的官 ...
- codeforces 518C. Anya and Smartphone
C. Anya and Smartphone time limit per test 1 second memory limit per test 256 megabytes input standa ...
- Photoshop技能167个经典的Photoshop技巧大全
Photoshop技能167个经典的Photoshop技巧大全 学PS基础:Photoshop 技能167个 经典的Photoshop技巧大全,如果你是初级阶段的水平,熟读此文并掌握,马上进阶为中级 ...
- 任何时候都适用的20个C++技巧
这些小技巧之所以特别,是因为这些信息通常吧不能在C++书籍或者网站上找到.比如说,成员指针,即使对于高级程序员也是比较棘手,和易于产生bugs的,是应该尽量避免的问题之一. <翻 by凌云健笔& ...
- WorkFlow4.0--入门到精通系列-专题索引
原文地址:http://www.cnblogs.com/hegezhou_hot/archive/2011/06/15/2081405.html 开篇 首先.非常感谢大家的支持和厚爱,才有了这个系列, ...
- 13个JavaScript图表(JS图表)图形绘制插件
转自:http://blog.jobbole.com/13671/ 1. Flash 过去是最佳解决方案,但很多人多在从那迁移: 2. 现代浏览器及其更强大的计算能力,使其在转化绘制实时数据方面的能力 ...
- 开发板怎样开启telnet服务
linux开发板开启telnet服务须要一下几个条件: 1.文件系统支持telnet busybox默认是把telnet和telnetd功能编进去了的,所以这一步一般都省了. 2.挂载devpts 挂 ...