C++动态内存分配

动态内存分布

C语言中的动态分配:
 malloc与free函数

内存区域

全局变量,静态数据,常量          data  area
所有类成员函数和非成员函数代码          code area
为运行函数分配的局部变量,函数参数,返回数据,返回地址等           stack area(栈)
动态内存分配区   heap area (堆)

C++中的内存分配:

C++的运算符new 与delete

在堆上生成对象时,需要自动调用构造函数;

在堆上生成的对象,在释放时需要自动调用析构函数

new与delete,malloc与free需要配对使用;

new[]和delete[]生成和释放对象数组;//必须配对使用

new和delete是运算符,malloc与free是函数调用;   new与delete可以重载,malloc与free不能进行重载

看看简单的测试程序:

 14 #include<iostream>
 15 using namespace std;
 16     
 17 class Test{
 18     public:
 19         Test():m_val(0)
 20     {
 21         cout<<"Test"<<endl;
 22     }
 23         ~Test(){
 24         cout<<"~Test"<<endl;
 25         }
 26     private: 
 27         int m_val;
 28 };  
 29 int main()
 30 {   
 31     {
 32         Test a;            
//a的生命周期仅在于两个大括号之间,a在栈区
 33     }
 34     cout<<"end of }"<<endl;
 35 
 36     Test *pVal = new Test();     //使用运算符new,直接调用类的构造函数
 37     delete pVal;                         //调用析构函数
 38     pVal=NULL;//编程规范

 39    return 0; 

 40 }

编译运行结果:

[hongfuhao@hongfuhao main]$ g++ main.cpp 
[hongfuhao@hongfuhao main]$ ./a.out 
Test
~Test
end of }
Test
~Test
[hongfuhao@hongfuhao main]$

new[]与delete[]   与new,delete不同:添加两行代码与之前进行比较

 14 #include<iostream>
 15 using namespace std;
 16 
 17 class Test{
 18     public:
 19         Test():m_val(0)
 20     {
 21         cout<<"Test"<<endl;
 22     }
 23         ~Test(){
 24         cout<<"~Test"<<endl; 
 25         }
 26     private:
 27         int m_val;
 28 }; 
 29 int main()
 30 {  
 31     {
 32         Test a;
 33     }
 34     cout<<"end of }"<<endl;
 35    
 36     Test *pVal = new Test();
 37     delete pVal;
 38     pVal=NULL;
 39     
 40     Test *pArray=new Test[2];             //调用两次构造函数
 41    
 42     delete [] pArray;    //调用两次析构函数
 43    
 44     return 0;
 45 }  
编译运行结果:

[hongfuhao@hongfuhao main]$ g++ main.cpp 
[hongfuhao@hongfuhao main]$ ./a.out 
Test
~Test
end of }
Test
~Test
Test
Test
~Test
~Test
[hongfuhao@hongfuhao main]$ 

上述代码如果单独调用delete,将42行代码改成  delete pArray;

[hongfuhao@hongfuhao main]$ g++ main.cpp 
[hongfuhao@hongfuhao main]$ ./a.out 
Test
~Test
end of }
Test
~Test
Test
Test
~Test
*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0000000000c6a018 ***
======= Backtrace: =========

/lib64/libc.so.6[0x3785675f4e]
./a.out[0x400afe]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x378561ed5d]
./a.out[0x4008e9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:02 403873                             /home/hongfuhao/main/a.out
00601000-00602000 rw-p 00001000 08:02 403873                             /home/hongfuhao/main/a.out
00c6a000-00c8b000 rw-p 00000000 00:00 0                                  [heap]
3784e00000-3784e20000 r-xp 00000000 08:02 1704388                        /lib64/ld-2.12.so
378501f000-3785020000 r--p 0001f000 08:02 1704388                        /lib64/ld-2.12.so
3785020000-3785021000 rw-p 00020000 08:02 1704388                        /lib64/ld-2.12.so
3785021000-3785022000 rw-p 00000000 00:00 0 
3785200000-3785283000 r-xp 00000000 08:02 1704402                        /lib64/libm-2.12.so
3785283000-3785482000 ---p 00083000 08:02 1704402                        /lib64/libm-2.12.so
3785482000-3785483000 r--p 00082000 08:02 1704402                        /lib64/libm-2.12.so
3785483000-3785484000 rw-p 00083000 08:02 1704402                        /lib64/libm-2.12.so
3785600000-378578a000 r-xp 00000000 08:02 1704389                        /lib64/libc-2.12.so
378578a000-378598a000 ---p 0018a000 08:02 1704389                        /lib64/libc-2.12.so
378598a000-378598e000 r--p 0018a000 08:02 1704389                        /lib64/libc-2.12.so
378598e000-378598f000 rw-p 0018e000 08:02 1704389                        /lib64/libc-2.12.so
378598f000-3785994000 rw-p 00000000 00:00 0 
378f200000-378f216000 r-xp 00000000 08:02 1704368                        /lib64/libgcc_s-4.4.7-20120601.so.1
378f216000-378f415000 ---p 00016000 08:02 1704368                        /lib64/libgcc_s-4.4.7-20120601.so.1
378f415000-378f416000 rw-p 00015000 08:02 1704368                        /lib64/libgcc_s-4.4.7-20120601.so.1
378fe00000-378fee8000 r-xp 00000000 08:02 1977349                        /usr/lib64/libstdc++.so.6.0.13
378fee8000-37900e8000 ---p 000e8000 08:02 1977349                        /usr/lib64/libstdc++.so.6.0.13
37900e8000-37900ef000 r--p 000e8000 08:02 1977349                        /usr/lib64/libstdc++.so.6.0.13
37900ef000-37900f1000 rw-p 000ef000 08:02 1977349                        /usr/lib64/libstdc++.so.6.0.13
37900f1000-3790106000 rw-p 00000000 00:00 0 
7f53fffae000-7f53fffb3000 rw-p 00000000 00:00 0 
7f53fffc8000-7f53fffcb000 rw-p 00000000 00:00 0 
7ffdceb3f000-7ffdceb54000 rw-p 00000000 00:00 0                          [stack]
7ffdcebee000-7ffdcebef000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
已放弃 (core dumped)
[hongfuhao@hongfuhao main]$ 

以上结果是C++中典型的一个出错结果;

只调用一次析构函数,然后崩溃,此时会造成内存泄漏

        内存区域

代码存放在代码区,数据则根据类型的不同存放在不同区域中

  Bss段:未初始化或者初始化为0的全局变量;

实例代码:bss.c

 14 int bss_array[1024*1024];
 15 
 16 int main(int argc,char * argv[])
 17 {
 18     return 0;
 19 }  
~     

编译运行:

[hongfuhao@hongfuhao main]$ gcc bss.c  -o bss
[hongfuhao@hongfuhao main]$ file bss
bss: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

[hongfuhao@hongfuhao main]$ ls -alh bss
-rwxrwxr-x. 1 hongfuhao hongfuhao
6.2K 11月 16 01:21 bss

[hongfuhao@hongfuhao main]$ strip bss                      //strip使文件代码变得更小
[hongfuhao@hongfuhao main]$ ls -alh bss
-rwxrwxr-x. 1 hongfuhao hongfuhao 4.1K 11月 16 01:22 bss

在程序运行周期内,bss数据一直都存在;//变程时尽量不使用全局变量

Data段存放初始化为非零的全局变量;

实例:

 14 int bss_array[1024*1024]={1};
 15 
 16 int main(int argc,char * argv[])
 17 {
 18     return 0;
 19 }
~  
  

文件大小:

[hongfuhao@hongfuhao main]$ gcc -o data bss.c 
[hongfuhao@hongfuhao main]$ file data
data: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
[hongfuhao@hongfuhao main]$ ls -alh data 
-rwxrwxr-x. 1 hongfuhao hongfuhao
4.1M 11月 16 01:30 data
[hongfuhao@hongfuhao main]$ 

文件大小变得很大

在程序整个运行周期内,data数据一直存在;

静态变量在第一次进入作用域时被初始化,以后不必再初始化;

静态成员变量在类之间共享数据,也是放在全局/静态数据区中。并且只有一份拷贝。

  在上篇博客,火星人游戏中有详细介绍:http://blog.csdn.net/hongfuhaocomon/article/details/53134963

常量数据区rodata区

            常量不一定放在rodata中,有些立即数直接和指令编码在一起,放在text(代码区)中。

字符串常量,编译器会去掉重复的字符串,保证只有一个副本。

常量是不能修改的

        字符串会被编译器自动放到rodata中,加上关键字const修饰的全局变量也放在rodata中。

典型例子代码:

 13 #include <stdio.h>
 14 #include <stdlib.h>
 15 int main()
 16 {
 17     char *p = "hello";
 18     char *p2 = "hello";
 19     if (p==p2 )
 20     {    printf("Smatr complier, set the string in one place!\n");
 21     }
 22 
 23     char *p3 = (char *)malloc(12);
 24     memset(p3,0,12);
 25     strcpy(p3,p2);
 26     printf("p3 = %s\n",p3);
 27     p3[1]= 'x';
 28     printf("p3 = %s\n",p3);
 29     free(p3);
 30     p3 = NULL;
 31     p[1]='x';
 32     return 0;
 33 }

编译运行:

[hongfuhao@hongfuhao ~]$ gcc -o hello.c  hello

[hongfuhao@hongfuhao ~]$ ./hello 
Smatr complier, set the string in one place!
p3 = hello
p3 = hxllo
段错误 (core dumped)
[hongfuhao@hongfuhao ~]$

出现段错误,即常量不可修改;


  栈中存储自动变量或者局部变量,以及传递的参数等;

  在一个函数内部定义一个变量,或者向函数传递参数时,这些变量和参数存储在栈上,当变量推出这些变量的作用域时,这些栈上的存储单元会被自动释放;

  堆是用户程序控制的存储区,存储动态产生的数据

  当用mallic / new 来申请一块内存区域或者创建一个对象时,申请的内存在堆上分配,需要记录得到的地址,并且在不需要的时候释放这些内存。

  栈一般很小,满足不了程序的逻辑的要求;

  对象的生命周期是指对象从创建到被销毁的过程,创建对象时要占用一定的内存。因此在整个程序占用的内存随着对象的创建与销毁动态的发生变化。

  变量的作用域决定了对象的生命周期。


  全局对象在man函数之前被创建,main退出后被销毁

静态对象和全局对象类似,第一次进入作用域时被创建,但是程序开始时,内存已经分配好;

 例子:

 13 #include <stdio.h>
 14 #include <stdlib.h>
 15 
 16 class A {
 17     public:
 18         A(){printf("A created\n");}
 19         ~A(){printf("A destroyed\n");}
 20 
 21 };
 22 
 23 class B{
 24     public:
 25         B(){printf("B created\n");}
 26         ~B(){printf("B destroyed\n");}
 27 };
 28 
 29 A globalA;
 30 
 31 B globalB;
 32 int foo()
 33 {
 34     printf("\nfoo()------------------------------------->\n");
 35     A localA;
 36     static B localB;
 37     printf("foo()<---------------------------------------\n");
 38     return 0;
 39 }
 40 int main()
 41 {
 42     printf("main()---------------------------------------->\n");
 43     foo();
 44     foo();
 45     printf("main()<----------------------------------------\n");
 46 
 47     return 0;
 48 }

编译运行:

[hongfuhao@hongfuhao task]$ g++ -o object object.cpp 
[hongfuhao@hongfuhao task]$ ./object 
A created
B created                                              
//main函数之前全局对象已被创建
main()---------------------------------------->


foo()------------------------------------->
A created                                                 //创建时调用构造函数
B created
foo()<---------------------------------------
A destroyed                                        //局部对象在函数退出时被释放         静态对象并未被释放


foo()------------------------------------->
A created
foo()<---------------------------------------
A destroyed
main()<----------------------------------------
B destroyed                                          //main函数退出后调用析构函数,全局对象与静态对象被释放
B destroyed                                                                      
A destroyed
[hongfuhao@hongfuhao task]$ 

 作用域是由{}定义,并不一定是整个函数

 通过new创建的对象,但容易造成内存泄露。通过new创建的对象一直存在,知道被delete销毁

  隐藏在中间的临时问变量的创建和销毁,生命周期很短,容易造成问题;(例如 :拷贝构造函数)

代码例子:

 14 #include <stdio.h>
 15 class A
 16 {
 17     public:
 18         
 19         A(){printf("A created\n");}
 20         ~A(){printf("A destroyed\n");}
 21         A(const A& a){printf( "A created with a copy\n");}                        
//拷贝构造函数
 22 };                                             
 23    
 24 A *foo(A a)
 25 {  
 26     printf("foo--------------->\n");
 27     A *p =new A();
 28     printf("foo<---------------\n");
 29     return p;
 30 }  
 31    
 32 A *boo(const A& a){
 33     A *p= new A();
 34     printf("boo<--------------\n");
 35     return p;
 36 }  
 37 int main()
 38 {  
 39     printf("main--------------------->\n");
 40     A a;
 41     A *p=foo(a);
 42    
 43     delete p;
 44     p=NULL;
 45            
 46     p=boo(a);
 47     delete p;
 48     p=NULL;
 49            
 50     printf("main()<---------------------\n");
 51            
 52     return 0;
 53 } 

编译运行:

[hongfuhao@hongfuhao task]$ g++ -o intebjc intebjc.cpp 
[hongfuhao@hongfuhao task]$ ./intebjc 
main--------------------->
A created                    
//40行代码创建一个对象a
A created with a copy  //41行代码,调用函数foo,传参数时传进去的是栈中a的一个副本,需要调用拷贝构造函数
foo--------------->          
A created                     //局部对象a的创建  
foo<---------------
A destroyed                //副本与局部对象的销毁,生命周期结束  自动释放
A destroyed
A created                    //调用函数boo,在函数内部创建一个局部对象a,调用构造函数
boo<--------------
A destroyed//局部对像被销毁,调用析构函数   自动释放
main()<---------------------
A destroyed                   //40行的对象a被销毁
[hongfuhao@hongfuhao task]$ 


补充一点:

拷贝构造函数的额调用:

当定义一个对象,并用同类型的对象去初始化该对象时,调用构造函数:

将该类型的对象传递给函数或从函数返回该类型的对象时,隐式调用拷贝构造函数;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

煮雨小哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值