目录
一、类在内存中的存储位置
- 类不会储存在内存中,类只是一个模板告诉程序,如何创建一个对象、需要多少个字节、以及如何销毁等等。
- 类实例化出的对象才会保存中内存中。
二、对象在内存中的存储位置
1. 全局对象
全局对象存放在全局(静态)存储区,程序结束后由系统调用析构函数释放。
2. 非静态局部对象
局部对象存放在栈中。其生命在作用域结束时结束,它的析构函数会自动被调用,即对象自动被清理。
const修饰的对象仅表示该对象的值是只读的,而不是对象本身存储的位置发生了变化。
3. 静态局部对象
静态局部对象存放在全局(静态)存储区,其生命在作用域结束之后仍然存在,即此时对象的析构函数并不会被调用,直到整个程序结束。
4. 动态对象
动态对象(即new出来的对象)存放在堆中,其生命在它被 delete 之际结束。用于创建动态对象的指针存放在栈中。
注:new 的对象,必须使用 delete 去显式的调用析构函数,否则程序不会去调用其析构函数,从而造成内存泄露。
【代码演示】
class A{ }; class B{ }; class C{ }; class D{ }; A a; //全局对象 int main(){ B b; //局部对象 static C c; //静态局部对象 D *d = new D; //动态对象 delete d; return 0; }
【内存布局】
三、成员函数的存储方法
成员函数存储方法猜想一:将成员函数的地址和成员变量都存在对象中
存在问题:调的成员函数都是一样的,所以没必要都每个对象都存一个成员函数的地址。
成员函数存储方法猜想二:将成员函数的地址单独存放进一张表中,然后存储时多存一个表的地址
这种方法没有什么问题,但没有被采用。
成员函数存储方法猜想三:对象中只存成员变量,将成员函数统一存放在公共代码区。
这个方案就是成员函数的存储方案,运行时不会在对象中找函数的地址,编译链接时才会根据函数名去公共代码区找到函数的地址。
总结:对象中只存成员变量,成员函数将统一存放在公共代码区。
所以类在遇到如下情况,程序不会崩溃。(因为找成员函数的地址不是在类中找,是去公共代码区找,所以类被实例化为nullptr我们依然能够调用成员函数)
四、成员变量在内存中的存储
【成员变量的内存布局探索】
在C++的对象的成员变量可分为两种:静态成员变量的和非静态成员变量,它们在C++的内存中是如何分布的呢?博主通过下面的代码进行了粗略的探索:
class Student { public: int name; int age; static int gender; }; // 在类外定义静态成员变量,并初始化为10。 int Student::gender = 10; int main() { Student a = Student(); cout << "对象的地址:" << &a << endl; cout << "非静态成员变量的地址:" << &(a.name) << "," << &(a.age) << endl; cout << "静态成员变量的地址: " << &(a.gender) << endl; return 0; }
通过代码的运行结果可看出:
非静态成员变量放在类的对象之中,且非静态成员变量在内存中的存放顺序与其在类内的声明顺序一致。静态成员变量存放在类的对象之外。即Student对象的内存分布如下图所示:
【总结】
1)类内的静态成员变量 ------------ 静态区
2)类内的非静态成员变量 ------------ 和对象的存储位置有关,类的非静态成员变量并不能决定自身的存储空间位置。决定存储位置的是对象的创建方式,即:
a)如果是全局对象,则对象,对象的成员变量保存在静态区。
b)如果是非静态局部对象,则对象,对象的成员变量保存在栈区。
c)如果是静态局部对象,则对象,对象的成员变量保存在静态区。
d)如果是动态对象,则对象,对象的成员变量保存在堆区。
(const修饰只是将变量限定为只读的,本质还是变量,存储位置看变量本身,所以const修饰的成员变量的存储位置仍要看对象的存储位置。)
无论是哪种方式创建对象,类的成员变量的存储位置都是相对于对象的起始位置来确定的。不同的成员变量可能会有不同的偏移量,这取决于编译器对类的内存布局的处理方式。
【代码演示】
// 以下代码用于演示类内的非静态成员变量的存储位置和对象的存储位置有关 class Student { public: int name; int age; }; Student a = Student(); //全局对象 int main() { Student b = Student(); //局部对象 static Student c = Student(); //静态对象 Student* d = new Student; //动态对象 cout << "全局对象的地址:" << &a << ",全局对象的成员变量的地址:" << &(a.name) << " " << &(a.age) << endl; cout << "局部对象的地址:" << &b << ",局部对象的成员变量的地址:" << &(b.name) << " " << &(b.age) << endl; cout << "静态对象的地址:" << &c << ",静态对象的成员变量的地址:" << &(c.name) << " " << &(c.age) << endl; cout << "动态对象的地址:" << d << ",静态对象的成员变量的地址:" << &(d->name) << " " << &(d->age) << endl; return 0; }
【静态成员变量的存储位置】
在C++中,类的静态成员变量被存储在全局/静态存储区。这些成员变量在类对象被创建之前就已经存在,并且在程序执行期间一直存在。
需要注意的是,静态成员变量只能被初始化一次,通常在类定义时完成。如果静态成员变量没有显式初始化,则会被默认初始化。对于数值类型的静态成员变量,默认初始化为0;对于引用类型的静态成员变量,默认初始化为空引用。但是需要注意的是const修饰和数组类型的静态成员变量的特殊情况,它们必须在类定义中进行显式初始化。
C++中的静态成员变量存储在类的全局数据区。静态成员变量在程序运行时只有一个实例,无论有多少个类的对象被创建,它们都共享同一个静态成员变量。因此,静态成员变量的存储空间位置是在类的数据段。
------------------------END-------------------------
才疏学浅,谬误难免,欢迎各位批评指正。