关于 delete 基本数据类型数组/对象数据/字符串 需不需要加 [ ],是否造成内存泄漏,怎么检测

一、前言

在这里插入图片描述
感谢评论,之前写的东西都好久没看过了,印象也不是很深刻,但这个问题的确问到了,反观这个文章啥也没写清楚,属实是垃圾,所以今天重新整理下,并附加一些例子进行实验;

直接抛出结果:

  1. new/delete 和 new[]/delete[] 要配对使用! 记住这个原则就行;
  2. 对于基本数据类型数组,实践证明 delete 和 delete[] 都没问题,但是使用 delete 时 Clion 会提示建议使用 delete[],通过内存泄漏检测可以发现,delete 会触发 MismatchedFree 警告,即使没有泄漏,所以建议用了 new[],就要用 delete [] 释放;
  3. 对于对象数组,必须使用 delete[] 释放,否则会造成内存泄漏,因为 delete 只释放第一个元素的内存,同时 内存泄漏测试提示 InvalidFree 并检测到泄漏了内存;
  4. 对于 new 出来的 string 类型的数据,释放时使用 delete 就行;

二、实验验证

2.1 实验环境

  • IDEA:Clion
  • C++ standard:C++14
  • 内存泄漏检测工具:Valgrind
  • 编译环境:Windows11(内存泄漏检测时 放在 WSL 中进行编译运行)

具体的安装适用于 Linux 的 Windows 子系统 WSL ,完成 Clion 中对内存泄漏检测工具 Valgrind 的配置 ,可以参考这篇文章:安装适用于 Linux 的 Windows 子系统 WSL ,完成 Clion 中对内存泄漏检测工具 Valgrind 的配置,亲测可用

2.2 基本数据类型数组 delete 实验

void funcChar1(const int num) {
    cout << "===== start ====" << endl;
    char *p = new char[num];
    cout << "===== delete p: ====" << endl;
    delete[] p;
    cout << "===== end ====" << endl;
}

void funcChar2(const int num) {
    cout << "===== start ====" << endl;
    char *p = new char[num];
    cout << "===== delete p: ====" << endl;
    delete p;
    cout << "===== end ====" << endl;
}

void funcInt1(const int num) {
    cout << "===== start ====" << endl;
    int *p = new int[num];
    cout << "===== delete p: ====" << endl;
    delete[] p;
    cout << "===== end ====" << endl;
}

void funcInt2(const int num) {
    cout << "===== start ====" << endl;
    int *p = new int[num];
    cout << "===== delete p: ====" << endl;
    delete p;
    cout << "===== end ====" << endl;
}

① funcChar1 (new char[] 数组,使用 delete[] 释放)执行结果
在这里插入图片描述
Valgrind 面板未提示内存泄漏,说明使用正确;

② funcChar2 (new char[] 数组,使用 delete 释放)执行结果
在这里插入图片描述
上图可以看出,Valgrind 是提示了 MismatchedFree 警告;这个警告是说你这个没有发生内存泄漏,但是你的 delete 和 new[] 不匹配;带有 summary 的 Valgrind 会在提示下面输出 summary:

HEAP SUMMARY:
==2955==   in use at exit: 0 bytes in 0 blocks
==2955==   total heap usage: 1 allocs, 1 frees, 20 bytes allocated
==2955== 
==2955==   All heap blocks were freed -- no leaks are possible (不会发生泄漏)

同样 funcInt1funcInt2 的结果跟 funcChar1funcChar2 类型

2.3 对象数组 delete 实验

// cbase.h
class cbase {
public:

    cbase();

    ~cbase();
    
private:
    int cdata;
};

// cbase.cpp
cbase::cbase() {
    cout << "constructure " << endl;
}

cbase::~cbase() {
    cout << "destructor " << endl;
}

// main.cpp
void funcObject1(const int num) {
    cout << "===== start ====" << endl;
    cbase *p = new cbase[num];
    cout << "===== delete p: ====" << endl;
    delete[] p;
    cout << "===== end ====" << endl;
}

void funcObject2(const int num) {
    cout << "===== start ====" << endl;
    cbase *p = new cbase[num];
    cout << "对象大小:" << sizeof(p[0]) << endl;
    cout << "===== delete p: ====" << endl;
    delete p;
    cout << "===== end ====" << endl;
}

int main() {
    const int NUM = 3;
    funcObject1(NUM);
    return 0;
}

① funcObject1 (new cbase[] 数组,使用 delete[] 释放)执行结果在这里插入图片描述
这里我是 new 的三个 cbase 对象,看结果,三个构造输出,三个析构输出,Valgrind 面板无内容,没毛病;

② funcObject2 ( new cbase[] 数组,使用 delete 释放)执行结果
在这里插入图片描述
首先,三个构造函数,只有一个析构函数,可能只释放了一个对象;
其次,对象数组的内存布局由 8字节的数组长度(不同环境可能不同,有的是4) + 各个元素占用字节数 组成;这里一共占用 20 字节,20=3*4+8;那为什么释放了一个对象却还是提示 20 字节内存泄漏呢?原因推测如下:
|8(未释放)|4(已释放)|4(未释放)|4(未释放)| 对于连续分配内存的堆而言,一个20字节的块,即使你在中间释放了 4 字节,但对于块而言,还是处于被占用的情况,所以这里 Valgrind 显示你内存泄露了 20 字节,即这个 20 字节的块 无法被其他程序 使用;

2.4 string delete 实验

void funcString1() {
    cout << "===== start ====" << endl;
    string *p = new string("aaa");
    cout << "===== delete p: ====" << endl;
    delete[] p;
    cout << "===== end ====" << endl;
}

void funcString2() {
    cout << "===== start ====" << endl;
    string *p = new string("aaa");
    cout << "===== delete p: ====" << endl;
    delete p;
    cout << "===== end ====" << endl;
}

① funcString1 (new string,使用 delete[] 释放)执行结果
在这里插入图片描述
说实话,这里我也不是很清楚为什么 一定泄露了 32 个字节,maybe:因为字符串"aaa"的长度为4,然后 delete[] p 把它当数组搞,释放了 4 个这样的内存,由于一个字符串 最少占 8 个字节,那么这里理所应当 非法读了 38=24 字节(但是 InvalidRead 又只显示 读了 28 =16 字节),泄露了 4*8=32 字节;有点不懂; InvalidRead 应该就是读取了未分配内存的区域,比如这里的后面 2个 或者 3个 (8字节的)内容;

后面有机会深入下,看能不能解决疑惑;

class{
	char *_Ptr; // 指向字符串的指针
	int _Len; // 字符串长度
}

总而言之,这样的作法肯定是不对的;

① funcString2 (new string,使用 delete 释放)执行结果在这里插入图片描述
正常的 配对着使用,就没问题;

三、总结

一个原则:new/delete 和 new[]/delete[] 要配对使用!,这样能在最大程度上避免内存泄漏的发生;

----------分割线:以前的文章---------

今天在写C++练习题时产生一个疑惑:
new出字符数组后 需不需要在delete时加 [ ]
按道理 只要是数组应该都要加的,但是答案没有加,于是晚上回来上机运行

环境:DevC++

#include<iostream>
#include<cstring>
using namespace std;
//有一些些成员是之前用到的 不过不影响测试
class cbase
{
	public:
		cbase(int i,char *p_r)//构造函数
		{
			m_data=i;
			cout<<"constructure of cbase.m_data="<<m_data<<endl;
			//new出连续内存用来复制 p_r所指向的内容
			p_name=new char[strlen(p_r)+1];
			strcpy(p_name,p_r);
		}
		cbase(cbase &a)//拷贝构造函数
		{
			m_data=a.m_data;
			p_name=new char[strlen(a.p_name)+1];
			strcpy(p_name,a.p_name);
		}
		~cbase()//析构函数
		{
			delete []p_name;//第一次加上[]
			//delete p_name;//第二次 不加[]
			cout<<"destructor of cbase.m_data="<<m_data<<endl;
		}
		void get_name()
		{
			cout<<"p_name="<<p_name<<endl;
		}
		protected:
			int m_data;
			char *p_name; 
};
int main()
{
	char str[10]="weishuai";
	cbase p(1,str);
	p.get_name();
	return 0;
}

运行结果:
第一次:加 [ ]
在这里插入图片描述
第二次:不加 [ ]
在这里插入图片描述
总结:
单单从这两次测试可以看出 对于new出来的字符数组 delete时加不加 [ ], 都能得到释放,并且系统并未给警告;
不过还有一种可能 就是不加 [ ]的话,会不会只delete了第一个字符呢?

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值