通过上面整个关于对象的基础知识框架,我们来分析两个例子,看一下在内存中,对象究竟长什么样。
class Point { public: Point( float xval ); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print( ostream &os ) const; float _x; static int _point_count; ]};
对于上面这个Point类,在内存中是怎么布置起来的呢?
根据以上这些问题,我们可以给出如下的对象布局方式:
由此可见,对于虚函数来说,它们由一个虚函数表统一管理,而在对象中只存放指向该虚函数表的指针。
在Demo1中,我们了解了一个对象在内存中是如何存放的,现在我们给它加上继承机制
即父类派生的子类的对象在内存中是如何布局的
看下面这段代码:
class ZooAnimal { public: ZooAnimal(); virtual ~ZooAnimal(); // ... virtual void rotate(); protected: int loc; String name; }; class Bear : public ZooAnimal { public: Bear(); ~Bear(); // ... void rotate(); virtual void dance(); //.... protected: enum Dances { ... }; Dances dances_known; int cell_block; }; class Panda : public Panda { // ........ };
这里使用了二层继承 ZooAnimal <--- Bear <--- Panda.
思考下面这段代码,在内存中的布局是怎样的呢?
{ ZooAnimal za; // 对象分配在栈上 ZooAnimal *pza; // 指针分配在栈上 Bear b; //对象分配在栈上 Panda *pp = new Panda // pp指针分配在栈上,对象new在堆中 pza = &b; }
根据注释上所说,我们在内存中的相应的位置,按顺序设想一下该段代码执行后内存中栈和堆的布局情况:
这个图中并没有考虑栈和堆的增长方向不同的因素,所以这里也就不深究。
栈中发生了什么很容易看得出,我们着重看一下在堆中的Panda对象的内存分布:
发现Panda对象被分成了三个部分,Panda对象 = Panda对象自己的一部分+Bear对象部分(Bear对象部分 = Bear对象自己的一部分 +基类ZooAnimal部分)
在栈中的Bear对象也是同样的道理。
由此图,我们大致了解了子类在内存中的布局。
因此,父类和子类之间的类型转换,即多态的实现,就可以认为是指针的覆盖范围的变换,而对内存布局没有影响。
简单探讨了一下对象在内存中的布局问题,以及如果通过布局来实现继承和多态的。
如果不正确的地方欢迎指正。
参考资料:
《深入理解C++对象模型》