• 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++中构建的每个对象都有自己的存储空间,但同一个类的所有对象共享同一组方法。在对象调用方法时,方法中将传递一个隐含的参数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所占用的堆内存。

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

  • 相关阅读:
    SQL 中单引号 和一些特殊字符的处理
    jquery 删除table行,该如何解决
    jQuery获取Select选中的Text和Value,根据Value值动态添加属性等
    C#中DataTable
    jquery操作select(取值,设置选中)
    JS刷新页面总和!多种JS刷新页面代码!
    VS中代码对齐等快捷键
    SQL递归查询(with cte as)
    SQL Server 公用表表达式(CTE)实现递归的方法
    linux ls和 ll 命令
  • 原文地址:https://www.cnblogs.com/guoyuanwei/p/2999575.html
Copyright © 2020-2023  润新知