C++对象模型
很久之前就想总结一下C++的内存使用机制。直到现在刚考完试之制,去实习之前,才有时间完成这事。
1.程序使用内存区
一个程序占用的内存区一般分为5种:
(1)全局、静态数据区:存储全局变量及静态变量(包括全局静态变量和局部静态变量)
(2)常量数据区:存储程序中的常量字符串等。
(3)代码区:存储程序的代码。
(4)栈:存储自动变量或者局部变量,以及传递的函数参数等。
(5)堆:存储动态产生的数据。
在处理内存时,系统会自动将内存对齐,这样虽然会浪费一些内存,但由于CPU在对齐方式下运行比较快,所以
一般都是对程序性能还是有好处的。
一个程序使用的栈的大小是固定的,由编译器决定。一般是1MB。
栈的内存是系统自动分配的,压栈和出栈都有相应的指令进行操作。因此效率较高,并且分配的内存空间是连
续的,不会产生内存碎片;而堆上的内存是由开发人员来动态分配和回收的。在分配内存时,系统需要按照一定
的算法在堆空间中寻找合适大小的空闲堆,并修改相应的维护堆空闲空间的链表,然后返回地址给程序。因此效
率比栈要低,此外还容易产生内存碎片。
从C++对象模型的角度来说,对象就是内存中的一片区域。如果一个对象通过定义在某个函数内的变量或者实现
需要的临时变量来创建时,它是栈上的一个对象;如果一个对象是定义在全局范围内的变量,则它是存储全局/静
态数据区;如果一个对象是通过new操作符来创建时,它是堆上的一个对象。
2.对象的生命周期
(1)通过定义变量创建对象:在这种情况下,变量的作用域决定了对象的生命周期。当进入变量的作用域时,对
象被创建。而退出变量的作用域时,对象被销毁。值得注意的是静态变量和全局变量,由于全局变量的作用域是整
个程序,因此被声明为全局变量的对象在程序调用main()函数之前被创建。当程序退出main()函数之后,全局对象
才被销毁。静态对象与全局对象类似,虽然静态变量的作用域不是整个程序,但静态变量是存储在全局/静态数据区
中,在程序开始时已经分配好。因此声明为静态变量的对象第一次进入作用域时被创建,直到程序退出时被销毁。
(2)通过new操作符创建对象:这种情况相对比较简单,但也最容易造成内存泄漏。通过new创建的对象会一直存在,
直到被delete销毁。即使指向该对象的指针(一般都是自动变量)已被销毁,但还没有调用delete,该对象就会一直
存在。即占据内存空间,直到程序退出,因此也就造成内存泄漏。
(3)通过实现创建对象:这种情况一般是指一些隐藏的中间临时变量的创建和销毁。它们的生命周期很短,也不容易
被开发人员发觉。但常常是造成程序性能下降的瓶颈,尤其是对于那些占用内存较多,创建速度较慢的对象。这些临时
对象一般是通过copy constructor创建的。在实际开发中,通过值传递传递参数,重载+及++等操作符,对对象进行算术
运算时,也会有临时对象,对于这些情况,都要尽量避免不必要的临时对象的出现。
3.C++对象的内存布局
(1)非静态数据成员是影响对象占据内存大小的主要因素,随着对象数目的增加,非静态数据成员占据的内存会相应增加。
(2)所有的对象共享一份静态数据成员,所以静态数据成员占据的内存的数量不会随着对象数目的增加而增加。
(3)静态成员函数和非静态成员函数不会影响对象内存的大小,虽然其实现会占据相应的内存空间,同样也不会随着对象
数目的增加而增加。
(4)如果对象中包含虚函数,会增加4个字节的空间(虚函数表指针),不论有多少个虚函数。
1.程序使用内存区
一个程序占用的内存区一般分为5种:
(1)全局、静态数据区:存储全局变量及静态变量(包括全局静态变量和局部静态变量)
(2)常量数据区:存储程序中的常量字符串等。
(3)代码区:存储程序的代码。
(4)栈:存储自动变量或者局部变量,以及传递的函数参数等。
(5)堆:存储动态产生的数据。
在处理内存时,系统会自动将内存对齐,这样虽然会浪费一些内存,但由于CPU在对齐方式下运行比较快,所以
一般都是对程序性能还是有好处的。
一个程序使用的栈的大小是固定的,由编译器决定。一般是1MB。
栈的内存是系统自动分配的,压栈和出栈都有相应的指令进行操作。因此效率较高,并且分配的内存空间是连
续的,不会产生内存碎片;而堆上的内存是由开发人员来动态分配和回收的。在分配内存时,系统需要按照一定
的算法在堆空间中寻找合适大小的空闲堆,并修改相应的维护堆空闲空间的链表,然后返回地址给程序。因此效
率比栈要低,此外还容易产生内存碎片。
从C++对象模型的角度来说,对象就是内存中的一片区域。如果一个对象通过定义在某个函数内的变量或者实现
需要的临时变量来创建时,它是栈上的一个对象;如果一个对象是定义在全局范围内的变量,则它是存储全局/静
态数据区;如果一个对象是通过new操作符来创建时,它是堆上的一个对象。
2.对象的生命周期
(1)通过定义变量创建对象:在这种情况下,变量的作用域决定了对象的生命周期。当进入变量的作用域时,对
象被创建。而退出变量的作用域时,对象被销毁。值得注意的是静态变量和全局变量,由于全局变量的作用域是整
个程序,因此被声明为全局变量的对象在程序调用main()函数之前被创建。当程序退出main()函数之后,全局对象
才被销毁。静态对象与全局对象类似,虽然静态变量的作用域不是整个程序,但静态变量是存储在全局/静态数据区
中,在程序开始时已经分配好。因此声明为静态变量的对象第一次进入作用域时被创建,直到程序退出时被销毁。
(2)通过new操作符创建对象:这种情况相对比较简单,但也最容易造成内存泄漏。通过new创建的对象会一直存在,
直到被delete销毁。即使指向该对象的指针(一般都是自动变量)已被销毁,但还没有调用delete,该对象就会一直
存在。即占据内存空间,直到程序退出,因此也就造成内存泄漏。
(3)通过实现创建对象:这种情况一般是指一些隐藏的中间临时变量的创建和销毁。它们的生命周期很短,也不容易
被开发人员发觉。但常常是造成程序性能下降的瓶颈,尤其是对于那些占用内存较多,创建速度较慢的对象。这些临时
对象一般是通过copy constructor创建的。在实际开发中,通过值传递传递参数,重载+及++等操作符,对对象进行算术
运算时,也会有临时对象,对于这些情况,都要尽量避免不必要的临时对象的出现。
3.C++对象的内存布局
(1)非静态数据成员是影响对象占据内存大小的主要因素,随着对象数目的增加,非静态数据成员占据的内存会相应增加。
(2)所有的对象共享一份静态数据成员,所以静态数据成员占据的内存的数量不会随着对象数目的增加而增加。
(3)静态成员函数和非静态成员函数不会影响对象内存的大小,虽然其实现会占据相应的内存空间,同样也不会随着对象
数目的增加而增加。
(4)如果对象中包含虚函数,会增加4个字节的空间(虚函数表指针),不论有多少个虚函数。