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

  • Post author:
  • Post category:其他




一、前言

在这里插入图片描述

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

直接抛出结果:


  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 (不会发生泄漏)

同样

funcInt1



funcInt2

的结果跟

funcChar1



funcChar2

类型



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 个字节,那么这里理所应当 非法读了 3

8=24 字节(但是 InvalidRead 又只显示 读了 2

8 =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了第一个字符呢?



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