对于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_;
};
内存布局以及虚函数列表:
没有继承,有虚函数,所以有了虚函数列表vfptr,指向该类的虚函数列表,虚函数列表中记录当前类的虚函数;
3、无虚函数,继承;
class simple1 : public simple {
public:
void simple1_func1() {};
private:
int simple1_a_;
};
内存布局:
普通的继承关系,而且基类中没有虚函数,所以不会生成虚函数列表,指向基类的和指向自身类的指针是同一个。
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_;
};
内存布局以及虚函数和列表:
单继承中,基类有虚函数,所以首先是虚函数列表,然后是从基类继承过来的成员变量,最后才是自己的成员变量。
由于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_;
};
内存布局:
从以上内存布局可以发现一些问题,就是属于Base的成员变量a_出现了两次,虽然生成的时候可以通过,但是在实际使用过程中容易造成二义性出错,所以这种用法是需要尽量避免的。
虚函数列表:
虚函数列表中也有同样的问题,两个虚函数列表,有重复的函数Base::func3(),在实际中也要尽量避免。
6、无虚函数,多继承,虚拟继承;
将以上的Derived类和Derived2类对Base的继承改为虚拟继承:
class Derived3 : public Derived, public Derived2 {
public:
void func6() { std::cout << "Func6()" << std::endl; }
private:
int d_;
};
内存布局:
最后一种情况使用了虚拟继承,内存布局就不同了,可以看出,一共有5个虚函数列表,在第一部分中(0~8),由两个虚函数列表和Derived的成员变量b_,第一个虚函数列表是Derived3类继承Derived类时生成的,第二个虚函数是Derived继承时Base生成的。
对于第二部分(12~20)同理,第三部分(24)则是Derived3自己的成员变量;
第四部分(28~32),则是Derived3间接继承Base生成的。
虚函数列表:
虚函数列表有点复杂,此处有点不明白,为什么会有负数出现,隐约能够看懂虚函数列表的指向。
从以上可以看到,在实例化一个类对象的时候,仅仅为类的成员变量和虚函数列表指针分配空间(虚函数的情况下),而成员函数属于整个类共有的,不会再对象中分配空间。因此,当虚函数更多的时候,占用的内存空间会更多,所以在实际项目中,关于类的继承体系的设计,需要考虑到这个方面。
如有问题,还请指正。