Graceful Code and Clean Soul

Seeking for a leap from greenhorn to guru in C++

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  2 随笔 :: 0 文章 :: 0 评论 :: 0 Trackbacks

2009年8月2日 #

这是Jan Gray在1994的一篇文章,是以Visual C++作为实现来讲解C++对象布局、继承、多态等的实现

下面是我的阅读笔记:

Intruduction

理解编程语言如何实现是非常重要的。这些关于语言实现的知识驱散了类似“编译器究竟在这里做了什么手脚?”之类的恐惧和疑问;给予了使用新特性的信心;并提供了调试和学习其他语言特性时的领悟力。它也提供了每天写最有效率代码的感觉。

本篇认真探索C++的底层,解释“运行时”实现细节,如类布局技术和virtual函数调用机制。将解释的问题包括:

  • 类如何内存布局?
  • 数据成员如何访问?
  • 成员函数如何调用?
  • 什么是adjuster thunk?
  • 代价如何:
    • 单继承、多继承、虚继承
    • 虚函数和虚函数调用
    • cast到基类或虚基类
    • 异常处理

Class Layout

本节,我们将考虑不同类型继承的内存布局

C-like Structs

和C是兼容的,struct遵循简单的结构体布局规则:按成员声明的顺序布局,并受制于实现定义的对齐填补

所有的C++厂商确保有效的struct由他们的C++编译器存储保持相同

例子:

image

struct A {
    char c;
    char i;
};

C-like Structs with C++ feature

这里B是一个带C++风格的的struct:有public/protected/private 访问控制符,成员函数,静态成员和嵌套的类型声明。

只有那些non-virtual 的数据成员在每个实例占有空间

image

struct B {
public:
    int bm1;
protected:
    int bm2;
private:
    int bm3;
    static int bsm;
    void bf();
    typedef void * bpv;
    struct N {};
};

Single Inheritance

image

struct C {
    int c1;
    void cf();
};

image

struct D : C {
    int d1;
    void df();
};

在D中,虽然没有要求说c的实例数据必须优先于D的实例数据,但是语言实现这样布局可以确保D中的基类子对象c的起始地址和D实例对象的起始地址一样,这样当我们需要从D*得到子对象地址时可以减少开销。

于是,在单继承的类层次中,新的实例数据成员仅仅是简单的追加到基类布局的后面。

Multiple Inheritance

在大多数设计中,单继承已经有足够表达力来表达继承关系。但是有时,我们希望继承类能取得两个或更多类的行为,多继承正好满足了这个要求。

image

struct E {
    int e1;
    void ef();
};

image

struct F : public C, E {
    inf f1; 
    void ef();
};

F继承自C和E,并且F的实例包含了每个基类的实例。但是不像单继承,不可能使得每个基类子对象的地址都和继承类的实例地址保持一样,其中只能有一个一样,其他的得做偏移。

既然存在地址偏移,我们注意到当我们做cast的时,对于需要地址偏移的cast显然有效率代价。

当然了,这种偏移对于语言使用者来说是透明的,偏移计算由编译器来实现。

Virtual Inheritance

struct G : virtual C {
    int g1;
    void gf();
}

image
struct H : virtual C {
    int h1;
    void hf();
}

image

struct I : G, H {
    int i1;
    void _if();
}

image

在G和H,C子对象的数据成员都是紧随各自数据成员之后。

但是当布局I时,我们不可能保留原有的位置关系,在上面例子中,对于G实例对象中,G到C的偏移与在I实例中的G到C的偏移并不相同。由于编译后,并不知道其从哪个类继承,于是必须有一种方法来计算继承类到虚基类的地址偏移。

在VC++中,这是通过添加一个隐藏的指针vbptr(虚基表指针)来实现的。它指向了一个类共享的地址偏移表。通常指向的虚基表中的第二项就是到虚基类的偏移

如图中的IdGvbptrC和IdHvbptrC

 

Data Member Access

posted @ 2009-08-02 16:02 Josh 阅读(427) | 评论 (0)编辑 收藏

2009年8月1日 #

终于注册到了我的英文名,Josh

本来想安家到cnblogs.com上,但是Josh却被人注掉了,无他,只好来到C++博客。

以下声明:

此博客关注范围为 -

  • C/C++ 语言层面/实现层面/标准库/第三方库
  • C++应用:GUI/网络编程等
  • Linux应用编程:shell/C++/软件安装使用,GNOME/GTK+/QT4
  • Linux内核:驱动/内核研究
  • 网络:计算机网络基础理论/协议栈分析/psocket, winsocket网络编程
  • 编译器/语言虚拟机:编译器理论,虚拟机研究
  • 汇编与机器:IA-32汇编,intel x86 CPU知识
  • 数据结构与算法
  • Windows编程
posted @ 2009-08-01 23:16 Josh 阅读(174) | 评论 (0)编辑 收藏

仅列出标题  
©2005-2009 Joshua org., All rights reserved.