首页 > 系统相关 >浅谈C/C++内存管理

浅谈C/C++内存管理

时间:2024-12-08 15:32:48浏览次数:12  
标签:long 浅谈 int C++ 内存 Test new delete

目录

一、C语言和C++内存管理方式

        1、C语言和C++内存管理方式区别 

        2、【new】和【delete】的好处

二、使用【new】来分配内存

三、使用【new】来创建动态数组

        1、创建动态数组

        2、使用动态数组 

四、使用【delete】来释放内存

        1、不要使用【delete】释放同一个内存两次

        2、不要使用【delete】来释放不是【new】开辟的内存

        3、匹配使用【delete】释放空间

五、【new】和【delete】对于自定义类型会调用构造函数和析构函数 


C

一、C语言和C++内存管理方式

        C语言内存管理方式:

        在C语言中使用库函数【malloc】、【calloc】、【realloc】、【free】来实现动态内存管理

        具体可以看一下此篇文章:C语言——动态内存分配_c语言 重新动态分配内存-CSDN博客

        C++内存管理方式:

        在C++中我们依然可以使用C语言中的库函数【malloc】、【calloc】、【realloc】、【free】来实现动态内存管理

        但是C++中提出了自己的内存管理方式:通过【new】和【delete】操作符进行动态内存管理

        1、C语言和C++内存管理方式区别 

相同点:

  1. 都是从堆上申请空间,并且需要用户手动释放

不同点:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放

        2、【new】和【delete】的好处

【new】/【delete】 相较于 【malloc】/【free】使用更加简单

【new】/【delete】的功能更加强大【new】/【delete】

二、使用【new】来分配内存

         我们要告诉【new】,需要为那种数据类型分配内存。【new】将找到一个长度合适的内存卡,并返回该内存块的地址。我们的责任就是将该地址赋予给一个指针

        格式:

        TypeName * Name = new TypeName 

        动态申请一个类型空间:

int main()
{
	// 动态申请一个类型的空间
	int* ptr1 = new int;
	double* ptr2 = new double;
	long long* ptr3 = new long long;
	Test* ptr4 = new Test;
	return 0;
}

class Test
{
public:
	Test(int n = 1)
		:num(n)
	{}
	int num;
};

         动态申请一个类型空间并初始化:

int main()
{
	// 动态申请一个类型的空间并初始化
	int* ptr1 = new int(1);
	double* ptr2 = new double(1.0);
	long long* ptr3 = new long long(1);
	Test* ptr4 = new Test(1);
	return 0;
}

class Test
{
public:
	Test(int n = 1)
		:num(n)
	{}
	int num;
};

三、使用【new】来创建动态数组

        1、创建动态数组

         在C++中,创建动态数组很容易。只要将数组的元素类型和元素个数告诉【new】就行。必须要在类型后面加上方括号,其中包含元素个数

        格式:

        TypeName* Name = new TypeName [ elements ] 

        使用【new】运算符可以确保内存块足以存储【elements】个类型的【TypeName】的元素,而【Name】指向了数组的第一个元素 

        动态申请一个数组: 

int main()
{
	// 动态申请一个数组
	int* ptr1 = new int[10];
	double* ptr2 = new double[10];
	long long* ptr3 = new long long[10];
	Test* ptr4 = new Test[10];
	return 0;
}

class Test
{
public:
	Test(int n = 1)
		:num(n)
	{}
	int num;
};

        动态申请一个数组并初始化: 

int main()
{
	// 动态申请一个数组并初始化
	int* ptr1 = new int[10] {1, 2, 3};
	double* ptr2 = new double[10] {1.0, 2.0, 3.0};
	long long* ptr3 = new long long[10] {1, 2, 3};
	Test* ptr4 = new Test[10]{ 1,2,3 };
	return 0;
}

class Test
{
public:
	Test(int n = 1)
		:num(n)
	{}
	int num;
};

【注意】

  •  动态申请一个数组并初始化,对于内置类型的不完全初始化编译器会自动补0
  •  动态申请一个数组并初始化,对于自定义类型的不完全初始化编译器会自动的调用构造函数进行初始化,没有相应的构造函数就会报错

        调试窗口:

        2、使用动态数组 

        C和C++内部都是使用指针来处理数组,而数组和指针基本等价。因此对于如何使用【new】运算符开辟的数组:只要把指针当作数组名使用即可 

         就是说,对于第一个元素可以使用【Name [ 0 ]】,而不是【*Name】;对于第二个元素可以使用【Name [ 1 ]】,而不是【*( Name + 1 )】,以此类推

        示例:

class Test
{
public:
	Test(int n = 1)
		:num(n)
	{}
	int num;
};
ostream& operator<<(ostream& out, const Test& t)
{
	out << t.num;
	return out;
}
int main()
{
	// 动态申请一个数组并初始化
	int* ptr1 = new int[10] {1, 2, 3};
	double* ptr2 = new double[10] {1.0, 2.0, 3.0};
	long long* ptr3 = new long long[10] {1, 2, 3};
	Test* ptr4 = new Test[10]{ 1,2,3 };
	//动态数组的访问
	for (int i = 0; i < 10; i++)
	{
		cout << ptr1[i] << " ";
	}
	cout << endl;
	//动态数组的访问
	for (int i = 0; i < 10; i++)
	{
		cout << *(ptr2 + i) << " ";
	}
	cout << endl;
	//动态数组的访问
	for (int i = 0; i < 10; i++)
	{
		cout << ptr3[i] << " ";
	}
	cout << endl;
	//动态数组的访问
	for (int i = 0; i < 10; i++)
	{
		cout << *(ptr4 + i) << " ";
	}
	cout << endl;
	return 0;
}

        结果: 

四、使用【delete】来释放内存

        当需要内存时,可以使用【new】来申请空间,当不需要【new】申请的内存时,可以使用【delete】来释放空间。使用【delete】时,后面要加上指向内存块的指针

        示例:

int main()
{
	// 动态申请一个内存空间
	int* ptr = new int;
	//释放空间
	delete ptr;
	return 0;
}

        【delete】将释放【ptr】指向的内存,但不会删除【ptr】指针本身,也就是说【delete】后的【ptr】为野指针。【prt】可以重新指向另一个动态开辟的内存 

规则:

  1. 不要使用【delete】来释放不是【new】开辟的内存
  2. 不要使用【delete】释放同一个内存两次
  3. 如果使用【new [ ] 】为数组开辟内存,应该使用【delete [ ] 】来释放内存
  4. 如果使用【new】为一个数据开辟内存,应该使用【delete】来释放内存
  5. 对空指针的应用【delete】是安全的

        1、不要使用【delete】释放同一个内存两次

int main()
{
	//不要使用【delete】释放同一个内存两次
	int* ptr = new int;

	delete ptr; //ok
	
	delete ptr; //erorr
	return 0;
}

        2、不要使用【delete】来释放不是【new】开辟的内存

int main()
{
	//不要使用【delete】来释放不是【new】开辟的内存
	int num = 10;

	int* ptr = &num;

	delete ptr; //erorr
	return 0;
}

        3、匹配使用【delete】释放空间

        如果使用【new】时带方括号,则使用【delete】时也因该带方括号。如果使用【new】时不带方括号,则使用【delete】时也因该不带方括号。它们之间要匹配使用

        【new】与【delete】的格式不匹配导致后果是不确定的,因此我们不能依赖于某种特定的行为

        示例: 

int main()
{
	//【new】与【delete】要匹配使用
	int* ptr1 = new int;
	int* ptr2 = new int[10];

	delete ptr2; //erorr
	delete[] ptr1; //erorr
	return 0;
}

五、【new】和【delete】对于自定义类型会调用构造函数和析构函数 

         【new】和【delete】对于内置类型相较于C语言只有用法上有区别,但是功能上区别不大

         【new】和【delete】对于自定义类型相较于C语言就有本质上的区别了,【new】和【delete】对于自定义类型除了开空间还会调用构造函数和析构函数,而【malloc】和【free】只会开空间

         示例:

class Test
{
public:
	//构造函数
	Test(int n = 1)
		:num(n)
	{
		cout << "构造函数" << endl;
	}
	//析构函数
	~Test()
	{
		cout << "析构函数" << endl;
	}
	int num;
};
int main()
{
	//new对于【自定义类型】除了开空间还会调用构造函数
	Test* ptr = new Test;
	//delete对于【自定义类型】会调用析构函数
	delete ptr;
	return 0;
}

        结果: 

        如果自定义类型没有默认构造函数,那么编译器就会报错 

        示例: 

class Test
{
public:
	//构造函数
	Test(int n)
		:num(n)
	{
		cout << "构造函数" << endl;
	}
	//析构函数
	~Test()
	{
		cout << "析构函数" << endl;
	}
	int num;
};

int main()
{
	//new对于【自定义类型】除了开空间还会调用构造函数
	Test* ptr = new Test;
	//delete对于【自定义类型】会调用析构函数
	delete ptr;
	return 0;
}

        结果:编译报错

标签:long,浅谈,int,C++,内存,Test,new,delete
From: https://blog.csdn.net/LVZHUO_2022/article/details/144170572

相关文章

  • vc++6.0与新版编译器多了哪些注意事项(1)
    VC6++,这是一个比较老的编译器了,对C++标准支持存在一些局限性,一.关于warningC4003:notenoughactualparametersformacro'getc'警告及相关错误原因分析:在C语言的标准库中有一个名为getc的宏(通常用于从文件流中读取字符等操作),代码里定义了名为getc的类point的......
  • 打卡信奥刷题(382)用C++信奥B3693[普及组/提高] 数列前缀和 4
    数列前缀和4题目背景这次不是数列的问题了。题目描述给定一个nnn行mm......
  • 嵌入式系统的内存访问和总线通信机制解析、PCI/PCIe引入
    往期内容Uart子系统专栏:专栏地址:Uart子系统Linux内核早期打印机制与RS485通信技术–末片,有专栏内容观看顺序interrupt子系统专栏:专栏地址:interrupt子系统Linux链式与层级中断控制器讲解:原理与驱动开发–末片,有专栏内容观看顺序pinctrl和gpio子系统专栏:专栏地......
  • 【C++算法】33.位运算_判定字符是否唯一
    文章目录题目链接:题目描述:解法C++算法代码:图解题目链接:面试题01.01.判定字符是否唯一题目描述:解法如果使用数据结构的话哈希表:一个一个字符扫描,不在哈希表里面的就放进去,在里面的就返回false。扫描完全部不重复就返回true。也可以优化一下,字母一共26......
  • 【C++算法】34.位运算_丢失的数字
    文章目录题目链接:题目描述:解法C++算法代码:题目链接:268.丢失的数字题目描述:解法哈希表创建一个0~5的数组从前往后遍历一下,有的数字就在表里面标记一下,最后看一下哪些数字没有被标记过。高斯求和先求出应该有的和:(首项+末项)*项数÷2然后减去数组的和......
  • Python内存管理的秘密,你必须知道的高效编程技巧!
    Python内存管理的秘密,你必须知道的高效编程技巧!前言亲爱的Python爱好者们,大家好!......
  • 关于c++的一个报错
    使用tstring构造函数,用到了VarBaseString的tostring,调用完,会导致局部对象指针为nullptr,目前在查原因classVarBaseString:publicVar{public:VarBaseString(std::stringstr=""){val=str;type="string";......
  • 内存泄漏和智能指针
    目录1.......
  • 斐波那契数列c++
    意大利数学家斐波那契(LeonardoFibonacci)是12、13世纪欧洲数学界的代表人物。他提出的“兔子问题”引起了后人的极大兴趣。“兔子问题”假定一对大兔子每一个月可以生一对小兔子,而小兔子出生后两个月就有繁殖能力,问从一对小兔子开始,n个月后能繁殖成多少对兔子?输入格式:首先......
  • 16-01、JVM系列之:内存与垃圾回收篇(一)
    JVM系列之:内存与垃圾回收篇(一)##本篇内容概述:1、JVM结构2、类加载子系统3、运行时数据区之:PC寄存器、Java栈、本地方法栈一、JVM与JAVA体系结构JAVA虚拟机与JAVA语言并没有必然的联系,它只是与特定的二进制文件格式:Class文件格式关联,Class文件中包含了JAVA虚拟机指令集......