一、在C++语言中内存主要分为如下5个存储区:
(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。
(2)堆(Heap):由new申请的内存,且由delete或delete[]负责释放。而C中程序员用malloc/calloc/realloc分配,free释放
(3)自由存储区(Free Storage):由程序员用malloc/calloc/realloc分配,free释放。
如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收。
(4)全局区/静态区(Global Static Area): 全局变量和静态变量存放区,程序一经编译好,该区域便存在。
在C++中,由于全局变量和静态变量编译器会给这些变量自动初始化赋值,所以没有区分了初始化变量和未初始化变量了。
需要说明一点,全局静态变量和局部静态变量都是存储在同一个静态区(全局区),只是作用域不同。程序结束后才释放空间。
(5) 常量存储区: 这是一块比较特殊的存储区,专门存储不能修改的常量(一般是const修饰的变量,或是一些常量字符串)。
从地址空间来看,上面的全局区与静态区可以统称为静态数据区,自由存储区和堆区类似,都要手动的分配与释放内存
C++中构建的每个对象都有自己的存储空间,但同一个类的所有对象共享同一组方法。在对象调用方法时,方法中将传递一个隐含的参数this,这个参数是个指向调用对象的指针。
二、对象的生命周期伴随这它在内存中的不同位置其生命周期不一样,生命周期的开始和结束会调用类的构造函数和析构函数;
但不同的时机会调用不同构造函数和析构函数:看下面代码,头文件中申明一个类,源文件中定义类
头文件定义如下:
#ifndef PEOPLE_H #define PEOPLE_H class people { public: people(); ~people(); people(const char *s,int a); void getName(void); private: char *name; int age; }; #endif
类的定义如下:
#include "people.h" #include <stdio.h> #include <string.h> people::people() //默认的构造函数 { printf("调用了默认的构造函数people()\n"); name = new char[strlen("no name")+1]; strcpy(name,"no name"); age = 10; } people::~people() { printf("析构函数被调用\n"); delete name; } people::people(const char *s,int a) //带参数的构造函数 { printf("调用了构造函数people(const char* s)\n"); name = new char[strlen(s)+1]; strcpy(name,s); age = a; } void people::getName(void) { printf("%s\n",name); }
主函数如下:
#include "people.h" #include <stdio.h> #include <stdlib.h> void test(void); int main(void) { people *p3 = new people("hello",100); test(); return 0; } void test(void) { people p1; }
从上面可知,析构函数的调用时机在对象的生命周期结束时,p1为函数块内的对象,在栈上分配,生命周期为函数块内,函数执行完后,就会调用析构函数了。虽然p1在
栈上,但是p1的成员name却通过new分配在堆上,因此必须通过析构函数中的delete来释放其占用的堆内存。反观对象p3,由于是通过new分配的,因此指针p3所指向的对象存储在堆上,
需要显示的调用delete p3才能释放p3所指向的堆内存,同时会调用析构函数释放其成员name所占用的堆内存。
同样的道理如果是静态存储类对象,析构函数将在程序结束时自动调用。总之析构函数的调用与对象的生命周期有关。