虽然C++面向对象很容易上手, 但是一直对C++对象的底层实现不知甚解, 得益于vs自带cl命令可以查看内存布局, 本文根据书上的引导在windows7 64位 vs2013环境下进行验证。
单一继承
代码
class Point {
public:
virtual ~Point();
virtual Point& mult(float) = 0;
float x() const { return _x; };
virtual float y() const { return 0; }
virtual float z() const { return 0; }
protected:
Point(float x = 0.0);
float _x;
};
class Point2d : public Point {
public:
Point2d(float x = 0.0, float y = 0.0) :
Point(x), _y(y)
{
}
~Point2d();
Point2d& mult(float);
float y() const { return _y; }
protected:
float _y;
};
class Point3d : public Point2d {
public:
Point3d(float x = 0.0, float y = 0.0, float z = 0.0) :
Point2d(x, y), _z(z)
{
}
~Point3d();
Point3d& mult(float);
float z() const { return _y; }
protected:
float _z;
};
int main(void)
{
return 0;
}
查看结果
单一继承还是比较简单的, 跟书中描述吻合。这里投个懒, 从网上找来一张图。
- 虚函数表在最前面的位置。
- 成员变量根据其继承和声明顺序依次放在后面。
- 在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。
多重继承
代码
class Base1 {
public:
Base1();
virtual ~Base1();
virtual void speackClearly();
virtual Base1* clone() const;
protected:
float data_Base1;
};
class Base2 {
public:
Base2();
virtual ~Base2();
virtual void mumble();
virtual Base2* clone() const;
protected:
float data_Base2;
};
class Derived : public Base1, public Base2 {
public:
Derived();
virtual ~Derived();
virtual Derived* clone() const;
protected:
float data_Derived;
};
int main(void)
{
return 0;
}
查看结果
多重继承使用图片是下面这个样子:
我们可以看到:
- 每个父类都有自己的虚表。
- 子类的成员函数被放到了第一个父类的表中。
- 内存布局中,其父类布局依次按声明顺序排列。
- 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
虚继承
代码
class B
{
public:
int ib;
char cb;
public:
B() :ib(0), cb('B') {}
virtual void f() { }
virtual void Bf() { }
};
class B1 : virtual public B
{
public:
int ib1;
char cb1;
public:
B1() :ib1(11), cb1('1') {}
virtual void f() {}
virtual void f1() {}
virtual void Bf1() {}
};
class B2 : virtual public B
{
public:
int ib2;
char cb2;
public:
B2() :ib2(12), cb2('2') {}
virtual void f() {}
virtual void f2() {}
virtual void Bf2() {}
};
class D : public B1, public B2
{
public:
int id;
char cd;
public:
D() :id(100), cd('D') {}
virtual void f() {}
virtual void f1() }
virtual void f2() {}
virtual void Df() {}
};
int main(void)
{
return 0;
}
先来看看单一虚继承
不看不知道, 一看吓一跳, 这里出问题了。
- B1, B各有一个虚函数表, vftable的反向偏移指向类的起始地址偏移, 如果有overwrite两张表都会更新;
- 有了vbptr指针, 有个vbtable表, 表的正向偏移指向超类B, 反向偏移指向类的起始地址偏移;
- **子类的多了一个vtordisp, 占据四字节, 可以参考C++中的vtordisp域, ** 满足:
- 虚继承;
- 派生类重写了父类虚函数;
- 有显示声明的构造函数或析构函数
子类中就会被添加vtordisp域;
- B类放在最后面;
再来看看类D
D遵守多重继承的风格, 不同之处是把B类放在最后面了。
好了, 总算明白了C++的类是怎么回事, 看《深度探索C++对象模型》, 结合实例, 总算明白了vs对C++对象的处理过程, 和书上描述有很大不同。有道是纸上得来终觉浅, 绝知此事要躬行。