C++内存布局与对象的生命周期

一、在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++内存布局与对象的生命周期_第1张图片

 

 

 

 

 

 

 

 

从地址空间来看,上面的全局区与静态区可以统称为静态数据区,自由存储区和堆区类似,都要手动的分配与释放内存

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;
}

C++内存布局与对象的生命周期_第2张图片
从上面可知,析构函数的调用时机在对象的生命周期结束时,p1为函数块内的对象,在栈上分配,生命周期为函数块内,函数执行完后,就会调用析构函数了。虽然p1在

栈上,但是p1的成员name却通过new分配在堆上,因此必须通过析构函数中的delete来释放其占用的堆内存。反观对象p3,由于是通过new分配的,因此指针p3所指向的对象存储在堆上,

需要显示的调用delete p3才能释放p3所指向的堆内存,同时会调用析构函数释放其成员name所占用的堆内存。

同样的道理如果是静态存储类对象,析构函数将在程序结束时自动调用。总之析构函数的调用与对象的生命周期有关。

 

 

 

你可能感兴趣的:(生命周期)