C++对象布局及多态之虚成员函数调用

在构造函数中调用虚成员函数,虽然这是个不很常用的技术,但研究一下可以加深对虚函数机制及对象构造过程的理解。这个问题也和一般直观上的认识有所差异。先看看下面的两个类定义。

struct C180
{
 C180() {
  foo();
  this->foo();
 }
 virtual foo() {
  cout << "<< C180.foo this: " << this << " vtadr: " << *(void**)this << endl;
 }
};
struct C190 : public C180
{
 C190() {}
 virtual foo() {
  cout << "<< C190.foo this: " << this << " vtadr: " << *(void**)this << endl;
 }
};

父类中有一个虚函数,并且父类在它的构造函数中调用了这个虚函数,调用时它采用了两种方法一种是直接调用,一种是通过this指针调用。同时子类又重写了这个虚函数。

我们可以来预测一下如果构造一个C190的对象会发生什么情况。

我们知道,在构造一个对象时,首先会按对象的大小得到一块内存(在heap上或在stack上),然后会把指向这块内存的指针做为this指针来调用类的构造函数,对这块内存进行初始化。如果对象有父类就会先调用父类的构造函数(并依次递归),如果有多个父类(多重继承)会依次对父类的构造函数进行调用,并会适当的调整this指针的位置。在调用完所有的父类的构造函数后,再执行自己的代码。

照上面的分析构造C190时也会调用C180的构造函数,这时在C180构造函数中的第一个foo调用为静态绑定,会调用到C180::foo()函数。第二个foo调用是通过指针调用的,这时多态行为会发生,应该调用的是C190::foo()函数。

执行如下代码:

C190 obj;
obj.foo();

结果为:

<< C180.foo this: 0012F7A4 vtadr: 0045C404
<< C180.foo this: 0012F7A4 vtadr: 0045C404
<< C190.foo this: 0012F7A4 vtadr: 0045C400

时间: 2024-10-30 10:42:18

C++对象布局及多态之虚成员函数调用的相关文章

C++对象布局及多态实现之成员函数的调用

从这部分开始我们除了利用内存的信息打印来进行探索外,更多的会通过跟踪和观察编译器产生的汇编代码来理解编译器对这些语言特性的实现方式.汇编方面知识的讨论超出了本文的范围,我只对和我们讨论相关的汇编代码进行解析.理解本文要讨论的知识并不需要有很完整的汇编知识,但必须了解起码的概念. 下面我们看看引入虚继承后的影响.为了有所对比我们首先看看普通成员函数的调用情况. 执行如下代码,它包括了对象的普通成员函数调用,类的静态成员函数调用.通过指针调用普通成员函数: C010 obj;PRINT_OBJ_AD

C++对象布局及多态实现探索之虚函数调用

我们再看看虚成员函数的调用.类C041中含有虚成员函数,它的定义如下: struct C041{C041() : c_(0x01) {}virtual void foo() { c_ = 0x02; }char c_;}; 执行如下代码: C041 obj;PRINT_DETAIL(C041, obj)PRINT_VTABLE_ITEM(obj, 0, 0)obj.foo();C041 * pt = &obj;pt->foo(); 结果如下: The detail of C041 is 14

C++对象布局及多态探索之菱形结构虚继承

这次我们看看菱形结构的虚继承.虚继承的引入本就是为了解决复杂结构的继承体系问题.上一篇我们在讨论虚继承时用的是一个简单的继承结构,只是为了打个铺垫. 我们先看看这几个类,这是一个典型的菱形继承结构.C100和C101通过虚继承共享同一个父类C041.C110则从C100和C101多重继承而来. struct C041{ C041() : c_(0x01) {} virtual void foo() { c_ = 0x02; } char c_;};struct C100 : public vir

C++对象布局及多态实现探索之虚继承

下面我们来看虚继承.首先看看这C020类,它从C010虚继承:} struct C010{ C010() : c_(0x01) {} void foo() { c_ = 0x02; } char c_;};struct C020 : public virtual C010{ C020() : c_(0x02) {} char c_;}; 运行如下代码,查看对象的内存布局: PRINT_SIZE_DETAIL(C020) 结果为: The size of C020 is 6The detail o

C++对象布局及多态实现的探索(二)

虚函数的类的对象布局(1) 如果类中存在虚函数时,情况会怎样呢?我们知道当一个类中有虚函数时,编译器会为该类产生一个虚函数表,并在它的每一个对象中插入一个指向该虚函数表的指针,通常这个指针是插在对象的起始位置.所谓的虚函数表实际就是一个指针数组,其中的指针指向真正的函数起始地址.我们来验证一下,定义一个无成员变量的类C040,内含一个虚函数. struct C040 { virtual void foo() {} }; 运行如下代码打印它的大小及对象中的内容. PRINT_SIZE_DETAIL

Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承

从MS Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout .看个复杂的例子吧(如下),现在假设我们想知道Derived类的对象布局,怎么办? 在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutDerived吧! class Co

C++对象布局及多态实现之带虚函数的类

如果类中存在虚函数时,情况会怎样呢?我们知道当一个类中有虚函数时,编译器会为该类产生一个虚函数表,并在它的每一个对象中插入一个指向该虚函数表的指针,通常这个指针是插在对象的起始位置.所谓的虚函数表实际就是一个指针数组,其中的指针指向真正的函数起始地址.我们来验证一下,定义一个无成员变量的类C040,内含一个虚函数. struct C040{ virtual void foo() {}}; 运行如下代码打印它的大小及对象中的内容. PRINT_SIZE_DETAIL(C040) 结果为: The

C++对象布局及多态实现的探索

前言 本文通过观察对象的内存布局,跟踪函数调用的汇编代码.分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等. 写这篇文章源于我在论坛上看到的一个贴子.有人问VC使用了哪种方式来实现虚继承.当时我写了一点代码想验证一下,结果发现情况比我想象的要复杂.所以我就干脆认真把相关的问题都过了一遍,并记录成本文. 我对于C++对象模型的知识主要来自于Lippman的书<Inside the C++ Object Model>,中译版为候捷翻的<深度探索C++对象模型>,中英版

C++对象布局及多态实现探索之内存布局

为了便于分析和观察对象的内存布局,我把代码生成时的结构成员对齐选项设置为1字节,默认为8字节.如果你在自己的工程下编译文中的代码,请做同样的设置.因为我写了一些函数打印对象中的布局信息,如果对象选项不是1字节,运行这些代码会出现指针异常错误. 普通类对象的内存布局 首先我们从普通类对象的内存布局开始.c000为一个空类,定义如下: struct c000 {}; 运行如下代码打印它的大小及对象中的内容. print_size_detail(c000) 结果为: the size of c000