C++类几种情况的内存布局

对于C++开发者来说,了解其内存是非常必要的,同时C++的多态(动态绑定)的原理也是很重要的。

C++内存布局:

需要了解的几种情况:

1、无虚函数,非继承;

class simple {
public:
	void simple_fun1() {};
	void simple_fun2() {};

private:
	int simple_a_;
};

内存布局:


没有虚函数,没有继承,所以只有成员变量需要分配空间。

2、有虚函数,非继承;

class Base {
public:
	virtual void func1() { std::cout << "Func1()" << std::endl; }
	virtual void func2() { std::cout << "Func2()" << std::endl; }
	virtual void func3() { std::cout << "Func3()" << std::endl; }

private:
	int a_;
};
内存布局以及虚函数列表:

C++类几种情况的内存布局_第1张图片

没有继承,有虚函数,所以有了虚函数列表vfptr,指向该类的虚函数列表,虚函数列表中记录当前类的虚函数;

3、无虚函数,继承;

class simple1 : public simple {
public:
	void simple1_func1() {};
private:
	int simple1_a_;
};

内存布局:

C++类几种情况的内存布局_第2张图片

普通的继承关系,而且基类中没有虚函数,所以不会生成虚函数列表,指向基类的和指向自身类的指针是同一个。

4、有虚函数,单继承;

class Derived : public Base {
public:
	virtual void func2() { std::cout << "derived Func2()" << std::endl; }
	virtual void func4() { std::cout << "Func4()" << std::endl; }

private:
	int b_;

};
内存布局以及虚函数和列表:

C++类几种情况的内存布局_第3张图片

单继承中,基类有虚函数,所以首先是虚函数列表,然后是从基类继承过来的成员变量,最后才是自己的成员变量。

由于override了基类的func2(),所以在虚函数列表中可以开看到,func2的域是Derived,而不再是基类Base。

5、有虚函数,多继承(非虚拟继承);

class Derived2 : public Base {
public:
	virtual void func1() { std::cout << "derived2 Func1()" << std::endl; }
	virtual void func5() { std::cout << "Func5()" << std::endl; }
private:
	int c_;
};

class Derived3 : public Derived, public Derived2 {
public:
	void func6() { std::cout << "Func6()" << std::endl; }

private:
	int d_;
};

内存布局:

C++类几种情况的内存布局_第4张图片

从以上内存布局可以发现一些问题,就是属于Base的成员变量a_出现了两次,虽然生成的时候可以通过,但是在实际使用过程中容易造成二义性出错,所以这种用法是需要尽量避免的。

虚函数列表:

C++类几种情况的内存布局_第5张图片

虚函数列表中也有同样的问题,两个虚函数列表,有重复的函数Base::func3(),在实际中也要尽量避免。

6、无虚函数,多继承,虚拟继承;

将以上的Derived类和Derived2类对Base的继承改为虚拟继承:

class Derived3 : public Derived, public Derived2 {
public:
	void func6() { std::cout << "Func6()" << std::endl; }

private:
	int d_;
};

内存布局:
C++类几种情况的内存布局_第6张图片

最后一种情况使用了虚拟继承,内存布局就不同了,可以看出,一共有5个虚函数列表,在第一部分中(0~8),由两个虚函数列表和Derived的成员变量b_,第一个虚函数列表是Derived3类继承Derived类时生成的,第二个虚函数是Derived继承时Base生成的。

对于第二部分(12~20)同理,第三部分(24)则是Derived3自己的成员变量;

第四部分(28~32),则是Derived3间接继承Base生成的。

虚函数列表:

C++类几种情况的内存布局_第7张图片

虚函数列表有点复杂,此处有点不明白,为什么会有负数出现,隐约能够看懂虚函数列表的指向。


从以上可以看到,在实例化一个类对象的时候,仅仅为类的成员变量和虚函数列表指针分配空间(虚函数的情况下),而成员函数属于整个类共有的,不会再对象中分配空间。因此,当虚函数更多的时候,占用的内存空间会更多,所以在实际项目中,关于类的继承体系的设计,需要考虑到这个方面。

如有问题,还请指正。

你可能感兴趣的:(C++,C++,内存布局)