首页 > 编程语言 >c++ new delete

c++ new delete

时间:2024-05-29 13:10:44浏览次数:24  
标签:int c++ 内存 析构 new delete 构造函数

 

=====================

delete是我们c++中的关键字,我们都知道它和new是配套使用的,有new就有delete不然会存在内存泄漏的问题。但是我们使用过delete的同学可能会发现delete有两种形式,一种是delete [],那这两个有什么区别呢?
1、delete 释放new分配的单个对象指针指向的内存

2、delete[] 释放new分配的对象数组指针指向的内存

3、delete处理单个类类型,先会调用析构函数,释放它所占资源,然后释放它所占内存空间。

4、delete处理数组类类型的时候,会对每一个数组对象都调用它们的析构函数,然后再释放它们所占用的内存空间。所以对于类类型的数组如果不调用delete[],那就只调用了下标为0的对象的析构函数,可能会产生问题。

5、两个都会释放所占内存,对于内置类型不管是数组还是单个对象,都可以混用,没有关系,因为对于内置类型,它只干一件事,就是释放它们所占内存

6、如果对于单个类对象,delete和delete[]都可以,因为delete是知道它要释放多大空间的,加不加[]括号的区别是对不对每个对象调用析构函数,如果只有一个的话,它就调用一次,所以没有关系。

示例1:
int *p =new int;
delete p; //ok p指向了有一个int的内置类型大小的空间,delete直接释放了它。
//delete []p; ok

示例2:
int *p = new int [10](); //ok
delete p; //ok 内置类型不需要调用析构函数
delete [] p; //ok 效果和上面一样

示例3:
int *p =new A () ;
delete p; //ok
delete []p;//ok

示例4:
int *p =new A[10]();
delete p; //不行,只调用p[0]的析构函数
delete [] p; //可以,会调用构造函数
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/Betty2017/article/details/78634257

================

new对象时,类名后加括号与不加括号的区别

 

【1】默认构造函数

关于默认构造函数,请参见随笔《类中函数

请看测试代码:

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 
 4 // 空类
 5 class Empty
 6 {
 7 };
 8 
 9 // 一个既有默认构造函数又有自定义构造函数的类
10 class Base
11 {
12 public:
13     Base()  // 默认构造函数
14     {
15         cout << " default Base construct " << endl;
16         m_nValue = 100;
17     }
18 
19     Base(int nValue)  // 自定义构造函数
20     {
21         cout << " custom Base construct " << endl;
22         m_nValue = nValue;
23     }
24 
25 private:
26     int m_nValue;
27 };
28 
29 // 一个具有复合默认构造函数的类
30 class custom
31 {
32 public:
33     custom(int value = 100)
34     {
35         cout << " default && custom construct " << endl;
36         m_nValue = value;
37     }
38 
39 private:
40     int m_nValue;
41 };
42 
43 int main()
44 {
45     Empty* pE1 = new Empty;
46     Empty* pE2 = new Empty();
47 
48     Base* pB1 = new Base;
49     Base* pB2 = new Base();
50     Base* pB3 = new Base(200);
51 
52     custom* pC1 = new custom;
53     custom* pC2 = new custom();
54 
55     delete pE1;
56     delete pE2;
57 
58     delete pB1;
59     delete pB2;
60     delete pB3;
61 
62     delete pC1;
63     delete pC2;
64 
65     return 0;
66 }
67 
68 // run out:
69 /*
70  default Base construct
71  default Base construct
72  custom Base construct
73  default && custom construct
74  default && custom construct
75 */
复制代码

至此足以。

【2】加括号与不加的区别

(1)加括号

  1. 若括号为空,即无实参项,那么理解为调用默认构造函数;

  2. 若括号非空,即有实参项,可以理解为调用重载构造函数,或复合默认构造函数。

(2)不加括号

  调用默认构造函数,或复合默认构造函数。

【3】默认构造函数 与 复合默认构造函数的区别

默认构造函数:编译器会为每一个类默认提供一个构造函数,称之为默认构造函数。默认构造函数一般参数为空。

复合默认构造函数:一个由用户自定义的所有形式参数都赋有默认值的构造函数,称之为复合默认构造函数。

两者联系:

一个类中,若一旦有一个用户自定义构造函数,那么由编译器提供的默认构造函数就不再存在。用户自定义的构造函数为默认构造函数的重载版。

默认构造函数不复存在时,用户必须为这个类再自定义一个复合默认构造函数(选所有自定义构造函数其中之一,把形式参数均赋默认值即可)。

不论自定义构造函数(即构造函数的重载版)有多少个,只允许有一个复合默认构造函数

============

一直对C++中的delete和delete[]的区别不甚了解,今天遇到了,上网查了一下,得出了结论。做个备份,以免丢失。

    C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
关于 new[] 和 delete[],其中又分为两种情况:(1) 为基本数据类型分配和回收空间;(2) 为自定义类型分配和回收空间。

请看下面的程序。

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream>; using namespace std;   class T { public:   T() { cout << "constructor" << endl; }   ~T() { cout << "destructor" << endl; } };   int main() {   const int NUM = 3;     T* p1 = new T[NUM];   cout << hex << p1 << endl;   //  delete[] p1;   delete p1;     T* p2 = new T[NUM];   cout << p2 << endl;   delete[] p2; }

 

大家可以自己运行这个程序,看一看 delete p1 和 delete[] p1 的不同结果,我就不在这里贴运行结果了。


    从运行结果中我们可以看出,delete p1 在回收空间的过程中,只有 p1[0] 这个对象调用了析构函数,其它对象如 p1[1]、p1[2] 等都没有调用自身的析构函数,这就是问题的症结所在。如果用 delete[],则在回收空间之前所有对象都会首先调用自己的析构函数。
    基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的;但是对于类对象数组,只能用 delete[]。对于 new 的单个对象,只能用 delete 不能用 delete[] 回收空间。
    所以一个简单的使用原则就是:new 和 delete、new[] 和 delete[] 对应使用。

====================

摘要

在C++中通过new动态分配的内存,必须要要用delete进行释放,而使用使用new[]申请的内存释放时,标准做发用delete[],但有时用delete也能正常释放。那到底delete带方括号[]和不带方括号[]有什么区别呢?以下通过实例代码,测试一下C++中通过new[] 创建的内存在释放时是否一定需要在delete后加[]

基本数据类型

对于基本数据类型(如char, int, unsigned int, short int等),如果动态申请一个数组类型,如下:

int *a = new int[10 * 1024 * 1024];
...
delete a;    // 方式1,不带方括号

int *b = new int[10 * 1024 * 1024];
...
delete [ ] b;    //方式2,带方括号
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以上示例代码,使用了两种方法分配了一个10M int类型的数组,实际占用空间是40M字节。
肯定会不少人认为方式1存在内存泄露,然而事实上是不会!
通过观察任务管理器中,进程占用的内存可以看到,以上两种方式在申请内存后,进程占用的内存会增加40MB,而释放后就会还原到分配前的内存占用。

针对基本数据类型,方式1和方式2均可正常工作,因为:基本的数据类型对象没有析构函数,并且new 在分配内存时会记录分配的空间大小,则delete时能正确释放内存,无需调用析构函数释放其余指针。因此两种方式均可。

自定义数据类型

对于自定义的class(类),通过new申请了一个对象数组,返回一个指针,对于此对象数组的内存释放,需要做两件事情:一是释放最初申请的那部分空间,二是调用析构函数完成清理工作。

案例1

我们来看一个示例:

#include <iostream>

using namespace std;

class TestClass
{
public:
    int a;
};

int main()
{
    TestClass* p1 = new TestClass[10 * 1024 * 1024]; 
    delete p1; //方式1,不带[] 

    TestClass* p2= new TestClass[10 * 1024 * 1024];
    delete[] p2; //方式2,带[] 

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

以上代码两种释放方式,都能正常释放,通过观察进程的内存占用,也能看出两种方法分配内存后进程占用的内存相应增加,两种方法delete后,内存都能相应的减少。

案例2

我们将案例1稍微修改一下,代码如下:

#include <iostream>

using namespace std;

class TestClass
{
public:
    int* p;

    TestClass()
    {
        p = new int[1];
    }
    ~TestClass()
    {
        delete[] p;
    }

};

int main()
{
    TestClass* p1 = new TestClass[10 * 1024 * 1024];
    delete p1; //方式1,不带[] 

    TestClass* p2= new TestClass[10 * 1024 * 1024];
    delete[] p2; //方式2,不带[] 

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

以上代码,将TestClass中修改为在类中有动态分配内存,并在析构时释放。方式1,直接会报如下异常信息:
image-1662692423300

继续执行会报如下错误:
image-1662692969494

而方式2就能正常释放,并且从任务管理器观察的内存变化情况来看,占用的内存也都相应的被释放掉了。

对于内存空间的清理,由于申请时记录了其大小,因此无论使用delete还是delete[]都能将这片空间完整释放,而问题就出在析构函数的调用上,当使用delete时,仅仅调用了对象数组中第一个对象的析构函数,而使用delete []的话,将会逐个调用析构函数。

异常原因

我们再来看如下代码

#include <iostream>

using namespace std;

class TestClass
{
public:

    TestClass() { cout << "constructor" << endl; }

    ~TestClass() { cout << "destructor" << endl; }

};

int main()
{
    const int NUM = 3;
    TestClass* p1 = new TestClass[NUM];

    cout << hex << p1 << endl; //输出P1的地址
                               // delete[] p1;

    delete p1;  //方式1,不带[]

    cout << endl;

    TestClass* p2 = new TestClass[NUM];
    cout << p2 << endl; //输出P2的地址
    delete[] p2;

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

输出结果为:

constructor
constructor
constructor
001174D4
destructor
  • 1
  • 2
  • 3
  • 4
  • 5

此时就直接报前文所说的异常了。
而将方式1的代码屏蔽掉,直接执行方式2的代码,输出结果如下:

constructor
constructor
constructor
00A13514
destructor
destructor
destructor
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到,不加[]时只调用了一次析构函数,然后就报异常了,而加了[]调用了上次析构函数。

总结

通过实例代码,测试了C++中通过new[] 创建的内存在释放时是否需要在delete后加[]。从测试结果来看,如果是基本数据类型,delete时加方括号和不加方括号都是一样的效果。如果是自定义的class,分两种情况:class中有动态分配的资源和没有动态分配的资源,有动态分配的资源时,也就是需要通过析构函数来释放动态分配的资源,则必须使用delete[],否则就会报异常(也可能开发环境并不会报异常,但由于没有成功调用析构函数释放资源,会造成内存泄漏);而没有动态分配的资源时,delete和delete[]都能正常释放掉内存。所以,通过new[]创建的内存,在释放时,最好还是使用delete[]的方式释放更加保险。

原文地址:https://www.cfnotes.com/archives/150

==============================================

参考:

https://www.cnblogs.com/Braveliu/p/4263145.html

 

标签:int,c++,内存,析构,new,delete,构造函数
From: https://www.cnblogs.com/rebrobot/p/18220044

相关文章

  • C++中的vector
    C++中的vector一丶vector的介绍和使用1.vector的简单介绍2.vector的使用I.构造函数II.相关的迭代器III.空间容量以及大小IV.vector的增删查改3.迭代器失效的问题二丶vector的深度剖析和模拟实现1.使用memcpy拷贝出现的问题2.vector的模拟实现一丶vector的介绍和......
  • C++ - 结构体转cha*
    c语言结构体转cha*在C语言中,将结构体转换为char*通常意味着你想要获取结构体的内存表示,并将其视为字符数组。这种转换可以通过使用memcpy函数来实现。下面是一个简单的例子,展示了如何将结构体转换为char*: #include<stdio.h>#include<stdlib.h>#include<string.......
  • C++ - tcp网络传输如何发送结构体类型
    1、tcp网络传输如何发送结构体类型 在C++中,要通过TCP网络传输结构体类型,你需要将结构体序列化为字节流,然后在另一端反序列化。这里有一个简单的例子:#include<iostream>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>//假设......
  • Re-Search for The Truth Multi-round Retrieval-augmented Large Language Models ar
    本文是LLM系列文章,针对《Re-SearchforTheTruth:Multi-roundRetrieval-augmentedLargeLanguageModelsareStrongFakeNewsDetectors》的翻译。重新寻找真相:多轮检索增强的大型语言模型是强大的假新闻检测器摘要1引言2相关工作3方法4实验5结论局限......
  • c++ string 使用例子
      ===============一文读懂C++String类在算法竞赛中的常见用法string相较于C语言的字符数组可方便太多了,在算法竞赛中能大大节省我们的时间。以下是我在刷题中会使用到的常见String用法。注释都写好了。#include<iostream>#include<string>usingnamespacestd;int......
  • 【C++】【YOLO】搭建环境运行YOLO模型,完成目标识别
    1、安装VisualStudio,勾选C++和Python负荷 2、安装CUDA|Pytorch|Python这三者之间的版本关系很重要详情参考:Pycharm搭建CUDA,Pytorch教程(匹配版本,安装,搭建全保姆教程)_cuda12.3对应的pytorch版本-CSDN博客3、下载ultralytics所有代码进行修改(https://github.com/ultralyt......
  • (免费领源码)Java/Mysql数据库+01012大学生爱心互助代购网站,计算机毕业设计项目推荐上万
    摘 要在网络信息的时代,众多的软件被开发出来,给用户带来了很大的选择余地,而且人们越来越追求更个性的需求。在这种时代背景下,企业只能以用户为导向,按品种分类规划,以产品的持续创新作为企业最重要的竞争手段。系统采用了B/S结构,将所有业务模块采用以浏览器交互的模式,选择My......
  • (免费领源码)Java/Mysql数据库+00895springboot的校园二手书销售平台,计算机毕业设计项目
    本科学生毕业设计校园二手书销售平台设计与实现                院系名称:    计算机科学与技术学院    专业班级:                            学生姓名:                           ......
  • (免费领源码)Java/Mysql数据库+00750基于python的音乐电台推荐系统设计,计算机毕业设计项
    毕业设计(论文)Django音乐电台推荐系统学   院:                           专   业:                           年   级:                           姓   名:   ......
  • (免费领源码)Java/Mysql数据库+00485 个性化音乐推荐系统的设计与实现,计算机毕业设计项
    毕业设计(论文)NodeJS个性化音乐推荐系统学   院:                           专   业:                           年   级:                           姓   名:  ......