【C++学习手札】new和delete看这一篇就够了!

news/2024/7/23 21:28:24 标签: c++, 学习

                                         食用指南:本文在有C基础的情况下食用更佳   

                                       🍀本文前置知识: C++类 

                                       ♈️今日夜电波: Prover—milet

                                                                1:21 ━━━━━━️💟──────── 4:01
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍 


目录

🌞引入—从C语言malloc、free到C++new、delete

🌔 一、new和delete语法定义

        new的语法定义

        delete的语法定义

        new的初始化

🌕二、给类对象申请空间(为什么说C++中要用new和delete)

        用malloc和用new给类申请空间的区别

         new申请对象数组

🌖三、一些注意事项

        delete void*可能会出错

        特别注意:malloc、free和new、delete 不可以混搭使用!

        使用 new 和 delete 采用相同形式


🌞引入—从C语言malloc、free到C++new、delete

        各位请先看下面这段代码:

void test1()//C语言动态管理空间
{
	int* p = NULL;

	p = (int*)malloc(sizeof(int));

	*p = 100;

	printf("%d\n", *p);
	free(p);
}

void test2()//C++动态管理空间
{
	int* p = NULL;

	p = new int(100);

	printf("%d\n", *p);
	delete p;
}

        这两段代码实现的功能是一样的。通过观察以及比对,我们很容易就能发现malloc和free分别对应着new和delete。乍一看,malloc、free和new、delete好像没什么区别?没错,他们的功能都是一样的,都是用来动态管理空间的。但是,如果细细观察,你会发现他们区别可大了!

         注意:

                1、new不需要强制类型转换。

                2、new可以在开辟空间时,可以同时初始化空间内容。

         \(^o^)/~ok,基本的引入就到这吧,现在我们进入正题,对于new、delete详解~


🌔 一、new和delete语法定义

        new的语法定义

//动态分配一个空间时
指针 = new 指针对应的类型;


//例1
int* ptr = new int;//在堆上分配一个整数的内存,并将其地址存储在指针ptr中


//申请多个空间时或者用于动态分配数组的内存时
指针 = new 指针对应的类型[申请的大小];

//例2

int* arr = new int[5];//在堆上分配一个包含5个整数的数组,并将其起始地址存储在指针arr中

        delete的语法定义

//当只开辟了一个空间时

delete 所要释放开间的指针

//例1
delete ptr;//释放空间


//申请多个空间时或者用于动态分配数组的内存时

delete 所要释放开间的指针

delete[] 所要释放空间的指针 //此释放该指针开辟的所有空间

//例2

delete arr;//可能只释放首个空间->不同编译器所为不同
delete[] arr;//全部释放

        特别注意:如果new和delete应当采用相同形式,详见本文末。

        new的初始化

//动态分配一个空间时
指针 = new 指针对应的类型(对应类型数值);

//例1
int* ptr = new int(100)//初始化空间值为100


//申请多个空间时或者用于动态分配数组的内存时
指针 = new 指针对应的类型[申请的大小]{数值(用,隔空)};

//例2
int* arr = new int[5]{1,2,3,4,5};//初始化5个空间值依次为1,2,3,4,5

        栗子:

void test2()//C++动态管理空间
{
	int* p = NULL;
	p = new int(100);
	printf("%d\n", *p);
	delete p;

	int* q = NULL;
	int* z = NULL;
	q = new int[5] {10, 20, 30, 40, 50};
	z = new int[5] {0};
	for (int i = 0; i < 5; i++)
	{
		cout << q[i] << " ";
	}
	cout << endl;
	for (int i = 0; i < 5; i++)
	{
		cout << z[i] << " ";
	}
	delete[]z;
	delete[]q;
}

🌕二、给类对象申请空间(为什么说C++中要用new和delete)

        用malloc和用new给类申请空间的区别

        请看下面这段代码~

class A
{
public:
	int num;

public:

	A()
	{
		num = 200;
		cout << "构造函数" << endl;
	}

	~A()
	{
		cout << "析构函数" << endl;
	}
};

void test()
{
	A* p = (A*)malloc(sizeof(A));
	p->num = 100;
	cout << "malloc:num=" << p->num << endl;
	free(p);
	cout << endl;

	A* q = new A;
	cout << "new:num=" << q->num << endl;
	delete q;
}

        以下为该段代码的结果:

        很明显的能看到,如果我们使用malloc开辟空间以及free来释放空间,类中最经典的构造函数和析构函数都是没有被调用的!然而,使用new来申请空间 如果申请成功 就会自动调用 对应类的构造函数,在用delete释放空间时会自动调用析构函数!因此,在C++中,我们使用new和delete会比malloc和free安全的多!

         new申请对象数组

         请看下面这段代码~

class A
{
public:
	int num;

public:

	A()
	{
		num = 100;
		cout << "无参构造函数num="<<num << endl;
	}

	A(int n)
	{
		num = n;
		cout << "有参构造函数num="<<num << endl;
	}

	~A()
	{
		cout << "析构函数num="<<num << endl;
	}
};

void test()
{
	cout << "无参:" << endl;
	A* arr1 = new A[5];
	delete[]arr1;

	cout << endl;

	cout << "有参:" << endl;
	A* arr2 = new A[5]{A(1),A(2),A(3),A(4),A(5)};
	delete[] arr2;
}

          以下为该段代码的结果: 

         以上的代码分别为利用无参构造函数和有参构造函数构造的对象数组,从以上例子我们也可总结出以下几点要点:

        1、类对象数组本质是数组 只是数组的每个元素是类的对象。

        2、如果想让对象数组中的元素调用有参构造 必须人为使用 有参构造初始化。
        3、初始化的元素 调用有参构造 没有初始化的 调用无参构造。

        4、当创建一个对象数组的时候, 必须对数组中的每一个对象调用构造函数, 除了在栈
        上可以聚合初始化, 必须提供一个默认的构造函数。

        这里建议回顾回顾类的知识:  构造函数与析构函数 (这是链接,快点!)

在对象数组中:

        每个元素自动调用构造和析构函数,而他的构造顺序和析构顺序入下图所示:

         构造按照入上文例子中有参构造,从左往右,也就是说谁先定义谁先构造,而析构则是相反,谁最后定义谁最先析构!


🌖三、一些注意事项

        delete void*可能会出错

        如果对一个 void*指针执行 delete 操作, 这将可能成为一个程序错误, 除非指针指
向的内容是非常简单的, 因为它将不执行析构函数.以下代码未调用析构函数, 导致可用内存减少。
所以尽量不要用delete释放void *!

           栗子:

class Person {
public:

	Person(char* name, int age) 
	{
		pName = (char*)malloc(sizeof(name));
		strcpy(pName, name);
		mAge = age;
	}

	~Person() {
		if (pName != NULL) {
			delete pName;
		}
	}
public:
	char* pName;
	int mAge;
};
void test() {
	char arr[] = "john";
	void* A = new Person(arr, 22);
	delete A;
}

 特别注意:malloc、free和new、delete 不可以混搭使用!

        使用 new 和 delete 采用相同形式

        请仔细看下面这段代码~

    Person* person = new Person[10];
    
    delete person;

        以上代码有什么问题吗?
        这里使用了两个编译器来对该代码进行运行,分别出现了以下错误:vs 下直接中断、 qt 下析构函数调用一次
        使用了 new 也搭配使用了 delete, 问题在于 Person 有 10 个对象, 那么其他 9 个对象可能没有调用析构函数, 也就是说其他 9 个对象可能删除不完全, 因为它们的析构函数没有被调用。 我们现在清楚使用 new 的时候发生了两件事: 一、 分配内存; 二、 调用构造函数, 那么调用 delete 的时候也有两件事: 一、 析构函数; 二、 释放内存。 那么刚才我们那段代码最大的问题在于: person 指针指向的内存中到底有多少个对象, 因为这个决定应该有多少个析构函数应该被调用。 换句话说, person指针指向的是一个单一的对象还是一个数组对象, 由于单一对象和数组对象的内存布局是不同的。 更明确的说, 数组所用的内存通常还包括“数组大小记录”, 使delete 的时候知道应该调用几次析构函数。 单一对象的话就没有这个记录。

        单一对象和数组对象的内存布局可理解为下图:

        本图只是为了说明, 编译器不一定如此实现, 但是很多编译器是这样做的。 当我们使用一个 delete 的时候, 我们必须让 delete 知道指针指向的内存空间中是否存在一个“数组大小记录”的办法就是我们告诉它。 当我们使用 delete[ ], 那么 delete就知道是一个对象数组, 从而清楚应该调用几次析构函数。 结论: 如果在 new 表达式中使用[ ], 必须在相应的

delete 表达式中也使用[ ].如果在 new 表达式中不使用[], 一定不要在相应的 delete 表达式

中使用 [ ]。


                   感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                        

                                                                        给个三连再走嘛~   ​​​​​​​  


http://www.niftyadmin.cn/n/4927977.html

相关文章

【剑指Offer 58】翻转单词顺序,Java解密。

LeetCode 剑指Offer 75道练习题 文章目录 剑指Offer:翻转单词顺序示例:限制:解题思路:剑指Offer:翻转单词顺序 【题目描述】 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a stu…

Dockerfile 简单实战

将flask项目打包成镜像 1. 准备flask文件 创建 app.py 文件&#xff0c;内容如下 from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello Worldif __name__ __main__:app.run(host0.0.0.0, port8000, debugTrue) 并开启外网访问&#xf…

【C++11】列表初始化 | decltype操作符 | nullptr | STL的更新

文章目录 一.列表初始化1. 花括号初始化2. initializer_list 二.decltype三.nullptr四.STL的更新1.STL新增容器2.字符串转换函数3.容器中的一些新方法 一.列表初始化 1. 花括号初始化 { }的初始化 C98中&#xff0c;标准允许使用大括号{}对数组或者结构体元素进行统一的列表初…

Endnote文献库重命名后无法打开解决办法

Endnote文献库重命名后无法打开解决办法 目录 Endnote文献库重命名后无法打开解决办法一、问题描述二、解决方法 本文给出Endnote文献库重命名后无法打开的解决方法 一、问题描述 有时需要对所建立的Endnote文献库需要重新命名&#xff0c;以适应实际需要。但是&#xff0c;单…

vue封装-获取当前时间

在开发时&#xff0c;经常遇到转换时间戳的问题&#xff0c;这里封装了一个方法&#xff0c;方便使用。 1.封装方法&#xff1a;src/utils/time.js /** Author: maxiaotiao* Description: 时间戳转换* FilePath: src/utils/time.js*/ class Time {// 1645839048000 --> 20…

带你玩转双链表

文章目录 前言一、双链表的思路二、带头循环双链表的实现分析二、带头循环双链表的实现11.带头循环双链表实现头文件总览2.带头循环双链表的初始化3.带头循环双链表的插入4.带头循环双链表的打印和销毁5.带头循环双链表的查找和删除 三、带头循环双链表的实现21.带头循环双链表…

【JAVA】static、工具类、代码块、单例、继承

1 static修饰成员变量 static是静态的意思&#xff0c;可以修饰成员变量和成员方法static修饰成员变量表示该成员变量只在内存中存储一份&#xff0c;可以被共享访问、修改 静态成员变量&#xff08;有static修饰&#xff0c;属于类&#xff0c;内存中只加载一次&#xff09;常…

哪个牌子的运动耳机好、最好的运动耳机推荐

在当今快节奏的生活中&#xff0c;运动已经成为许多人追求健康与活力的重要组成部分。而在运动过程中&#xff0c;一款出色的运动耳机不仅能为我们提供激励和动力&#xff0c;还能让我们沉浸在音乐的世界中&#xff0c;享受极致的运动体验。然而&#xff0c;市场上琳琅满目的运…