C++之new与delete的用法

时间:2023-01-29 14:42:37

起初刚学C++时,很不习惯用new,后来看老外的程序,发现几乎都是使用new,想一想区别也不是太大,但是在大一点的项目设计中,有时候不使用new的确会带来很多问题。当然这都是跟new的用法有关的。new创建类对象,使用完后需使用delete删除,跟申请内存类似。所以,new有时候又不太适合,比如在频繁调用场合,使用局部new类对象就不是个好选择,使用全局类对象或一个经过初始化的全局类指针似乎更加高效。

C++ 对象实例化的一些概念
C++ 如果直接定义类,如classA  a; a存在栈上(也意味着复制了对象a在栈中);
如果classA  a = new classA就存在堆中。

一、new创建类对象与不new区别

下面是自己总结的一些关于new创建类对象特点:

  • new创建类对象需要指针接收,一处初始化,多处使用
  • new创建类对象使用完需delete销毁
  • new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
  • new对象指针用途广泛,比如作为函数返回值、函数参数等
  • 频繁调用场合并不适合new,就像new申请和释放内存一样

二、new创建类对象实例

1、new创建类对象例子:

CTest* pTest = new CTest();

delete pTest;

pTest用来接收类对象指针。

不用new,直接使用类定义申明:

CTest mTest;

此种创建方式,使用完后不需要手动释放,该类析构函数会自动执行。而new申请的对象,则只有调用到delete时再会执行析构函数,如果程序退出而没有执行delete则会造成内存泄漏。

2、只定义类指针

这跟不用new申明对象有很大区别,类指针可以先行定义,但类指针只是个通用指针,在new之前并为该类对象分配任何内存空间。比如:

CTest* pTest = NULL;

但使用普通方式创建的类对象,在创建之初就已经分配了内存空间。而类指针,如果未经过对象初始化,则不需要delete释放

3、new对象指针作为函数参数和返回值

下面是天缘随手写一个例子,不太严谨。主要示意一下类指针对象作为返回值和参数使用。

[cpp]  view plain  copy
  1. class CTest {  public:   int a;  };      
  2. class CBest {  public:   int b;  };      
  3. CTest* fun(CBest* pBest) {    
  4.                            CTest* pTest = new CTest();     
  5.                            pTest->a = pBest->b;   return pTest;    
  6. }      
  7. int main() {    
  8.              CBest* pBest = new CBest();     
  9.              CTest* pRes= fun(pBest);        
  10.              if(pBest!=NULL)      
  11.              delete pBest;     
  12.              if(pRes!=NULL)      
  13.              delete pRes ;     
  14.              return 0;    
  15. }  

C++对象实例化

[cpp]  view plain  copy
  1. JAVA:  
  2. A a = new A();  
  3. 为A对象创建了一个实例,但在内存中开辟了两块空间:一块空间在堆区,存放new A()这个对象;  
  4. 另一块空间在堆栈,也就是栈,存放a,a的值为new A()这个对象的内存地址。因为java在JVM中运行,  
  5. 所以a 描述的内存地址不一定是这个对象真实内存的地址。  
  6.   
  7. Object o; // 这是声明一个引用,它的类型是Object,他的值为null,还没有指向任何对象,该引用放在内存的栈区域中。  
  8.   
  9. o = new Object(); // new Object()句,实例化了一个对象,就是在堆中申请了一块连续空间用来存放该对象。  
  10. // 运算符,将引向o指向了对象。也就是说将栈中表示引用o的内存地址的内容改写成了Object对象在堆中的地址。  
  11.   
  12. C++:  
  13. C++ 如果直接定义类,如classA a; a 存在栈上(也意味着复制了对象a在栈中),如果classA a = new classA就存在堆中。  

对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。

    new和delete运算符是用于动态分配和撤销内存的运算符。

一、new用法

1.开辟单变量地址空间

   使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。

    new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。

 一般使用格式:
        格式1:指针变量名=new 类型标识符;
        格式2:指针变量名=new 类型标识符(初始值);
        格式3:指针变量名=new 类型标识符 [内存单元个数];

说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。例如:

    1)new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a

    2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5

2.开辟数组空间

    对于数组进行动态分配的格式为:

       指针变量名=new 类型名[下标表达式];
       delete [ ] 指向该数组的指针变量名;

    两式中的方括号是非常重要的,两者必须配对使用,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的指针,会产生回收不彻底的问题(只回收了第一个元素所占空间),加了方括号后就转化为指向数组的指针,回收整个数组。

    delete []的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。

    请注意“下标表达式”不必是常量表达式,即它的值不必在编译时确定,可以在运行时确定。

    一维: int *a = new int[100];    //开辟一个大小为100的整型数组空间

    二维: int **a = new int[5][6]

    三维及其以上:依此类推.

    一般用法: new 类型 (初值)

二、delete用法

1. 删除单变量地址空间

       int *a = new int;

       delete a;   //释放单个int的空间

2. 删除数组空间

       int *a = new int[5];

       delete []a;    //释放int数组空间

三、使用注意事项

1. new 和delete都是内建的操作符,语言本身所固定了,无法重新定制,想要定制new和delete的行为,徒劳无功的行为。

2. 动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。

3. 指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。

4. 内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。

5. 动态分配的变量或对象的生命期。我们也称堆空间为*空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。

6. 要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。

    用new和delete可以动态开辟和撤销地址空间。在编程序时,若用完一个变量(一般是暂时存储的数据),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它。