当前位置: 首页 > article >正文

C26.【C++ Cont】动态内存管理和面向对象的方式实现链表

🧨🧨🧨🧨🧨🧨🧨🧨🧨除夕篇🧨🧨🧨🧨🧨🧨🧨🧨🧨

目录

1.知识回顾

2.两个操作符

new操作符

示例代码1:申请一个变量

示例代码2:申请连续的内存空间

delete操作符

示例代码3


1.知识回顾

C语言中的动态内存管理知识回顾:

68.【C语言】动态内存管理(重点)(1)

69.【C语言】动态内存管理(重点)(2)

70.【C语言】动态内存管理(重点)(3)

71.【C语言】动态内存管理(重点)(4)

E37.【C语言】动态内存管理练习题

2.两个操作符

和C语言一样,在堆区上自行申请空间和释放空间,注意:new和delete应该配对使用

new操作符

new申请一个变量的空间,new[ ]申请一个数组的空间

new返回的是申请到的内存空间的起始地址,因此需要指针来接收

示例代码1:申请一个变量

格式:new 数据类型 或 new 数据类型(初始值)

//p1指向申请的int类型的空间,但未初始化
int* p1 = new int;
if (p1==nullptr)
{
    perror("new");
    return 1;
}

//p2指向申请的int类型的空间,已初始化为5
int* p2 = new int(5); 
if (p2==nullptr)
{
    perror("new");
    return 1;
}

注:为防止new开辟空间失败,建议判断p1和p2是否为空,或者assert断言

VS2022调试模式下在内存窗口输入&p1

再跳到0x0128a288查看,4字节的cd,显然未初始化

VS2022调试模式下在内存窗口输入&p2

再跳到0x0128a2b8查看,为05 00 00 00,已初始化为5

示例代码2:申请连续的内存空间

申请连续的内存空间即申请数组空间

格式:new 数据类型[元素个数]

int* p3 = new int[10];//申请10个int类型的空间

注:new不是只能给内置类型开辟空间,也可以给自定义类型开辟空间,例如结构体

delete操作符

delete负责释放一个变量的空间,delete[ ]释放一个数组的空间

格式:delete 指针 或 delete[] 指针(数组的空间)

示例代码3

	int* p1 = new int;
    if (p1==nullptr)
    {
        perror("new");
        return 1;
    }
    int* p2 = new int(5);
    if (p2==nullptr)
    {
        perror("new");
        return 1;
    }
	int* p3 = new int[10];
    if (p1==nullptr)
    {
        perror("new");
        return 1;
    }

	delete p1;
	p1 = nullptr;
	delete p2;
	p2 = nullptr;
	delete[] p3;
	p3 = nullptr;

要养成良好的习惯,delete后防止野指针要手动为指针置空

有关malloc和new,delete和free的区别这里不做讲解,竞赛中只需要知道怎么用就行

注意:在竞赛中数据结构的模拟实现不会采用malloc和free或者 new 和 delete 的方式,代码量大和运行速度慢而是采用静态的足够大的数组来模拟实现

3.面向对象的方式实现链表

1.知识回顾

63.【C语言】再议结构体(上)
86.【C语言】数据结构之链表的总体概述

87.【C语言】数据结构之链表的头插和尾插

91.【C语言】数据结构之单向链表的头删和尾删

92.【C语言】数据结构之单向链表的查找,中间插入和删除,销毁

上面的这些文章实际上是面向过程的方式实现链表

2.面向对象的方式实现

竞赛中一般使用数组去模拟链表,这里简单了解就行

面向对象的方式:可以使用结构体去封装链表的各个操作(隐藏对象的内部实现细节,只暴露必要的接口供外部使用),之后借助点操作符去调用成员函数

(有关C++结构体的内容参见C24.【C++ Cont】结构体文章)

实现代码

struct List
{
	Node* phead;//指向头节点的指针
	Node* ptail;//指向尾节点的指针

	List()//构造函数用于初始化指向头节点的指针
	{
		phead = nullptr;
		ptail = nullptr;
	}

	~List()//析构函数用于销毁链表
	{
		while (phead)
		{
			SLTPopFront();
		}
		phead = nullptr;
		ptail = nullptr;
	}

	Node* BuySLTNode(SLTDataType x)//新建节点
	{
		Node* newnode = new  Node;
		if (newnode == nullptr)
		{
			perror("new");
			return nullptr;
		}
		newnode->val = x;
		newnode->next = nullptr;
		return newnode;
	}

	void SLTPushFront(SLTDataType x)//头插节点
	{
		Node* newnode = BuySLTNode(x);
		if (phead == nullptr)
		{
			phead = ptail= newnode;
		}
		else
		{
			newnode->next = phead;
			phead = newnode;//不用改动ptail
		}
	}

	void SLTPushBack(SLTDataType x)//尾插节点
	{
		Node* newnode = BuySLTNode(x);
		if (phead == nullptr)
		{
			ptail = phead = newnode;//如果是第一次插入需要同时更新首尾指针
		}
		else
		{
			ptail->next = newnode;
			ptail = ptail->next;
		}
	}

	void SLTPopFront()//头删节点
	{
		if (phead == nullptr)
		{
			cout << "链表为空,禁止头删" << endl;
			return;
		}
		Node* tmp = phead;
		phead = tmp->next;
		delete tmp;
		tmp = nullptr;
	}

	void SLTPopBack()//尾删节点
	{
		if (phead == nullptr)
		{
			cout << "链表为空,禁止尾删" << endl;
			return;
		}
		Node* tmp = phead;
		if (phead->next == nullptr)
		{
			delete phead;
			ptail= phead = nullptr;//删除最后一个节点,注意将首尾指针都置为空
			return;
		}
		while (tmp->next != ptail)//寻找尾节点前面的节点
		{
			tmp = tmp->next;
		}
		delete ptail;
		ptail = tmp;
		ptail->next = nullptr;
		tmp = nullptr;
	}

	void SLTPrint()//打印链表的所有节点
	{
		Node* cur = phead;
		while (cur)
		{
			cout << cur->val << "-->";
			cur = cur->next;
		}
		cout << "NULL"<<endl;
	}
};

如果用类写:

class List
{
    private:
	Node* phead;
	Node* ptail;

	public:
    List()
	{
        //......
	}

	~List()
	{
        //......
	}

	Node* BuySLTNode(SLTDataType x)
	{
        //......
	}

	void SLTPushFront(SLTDataType x)
	{
        //......
	}

	void SLTPushBack(SLTDataType x)
	{
		//......
	}

	void SLTPopFront()
	{
        //......
	}

	void SLTPopBack()
	{
        //......
	}

	void SLTPrint()
	{
        //......
	}
};

封装后的接口说明

只需要向开发者提供接口说明,供开发者调用,例如:

/
//文件名:list.cpp
//作者:zhancod
//版本:v1.0
//完成日期:2025.1.28
//修改记录:......
//
//描述:单链表API
//提醒:1.使用前先创建链表(List list;),之后调用接口函数(list.函数(?))
//     2.链表存储的数据类型定义在typedef  int SLTDataType;
//     3.不需要手动new和delete链表
//
//1.函数:SLTPushFront
//  功能:头插节点
//  使用方式:list.SLTPushFront(data)
//
//2.函数:SLTPushBack
//  功能:尾插节点
//  使用方式:list.SLTPushBack(data)
//
//3.函数:SLTPopFront
//  功能:头删节点
//  使用方式:list.SLTPopFront()
//
//4.函数:SLTPopBack
//  功能:尾删节点
//  使用方式:list.SLTPopBack()
//
//5.函数:SLTPrint
//  功能:打印链表的所有节点
//  使用方式:list.SLTPrint()
//

调用接口的代码

int  main()
{
	{
		List list;
		list.SLTPushFront(1);
		list.SLTPushBack(2);
		list.SLTPushFront(3);
		list.SLTPushBack(4);
		list.SLTPushFront(5);
		list.SLTPopFront();
		list.SLTPrint();
		list.SLTPopBack();
		list.SLTPrint();
		list.SLTPopBack();
		list.SLTPopFront();
		list.SLTPopBack();
		list.SLTPopFront();
		list.SLTPopBack();
		list.SLTPrint();
	}
	return 0;
}

执行结果

练习

稍微修改上方的代码可以提交本题:707. 设计链表 - 力扣(LeetCode)

之后会单独出一篇文章讲解

🎉❤️🎉❤️🎉❤️🎉❤️🎉❤️祝各位码农们蛇年大吉 巳巳如意!❤️🎉❤️🎉❤️🎉❤️🎉❤️🎉


http://www.kler.cn/a/524789.html

相关文章:

  • NLP模型大对比:Transformer > RNN > n-gram
  • 【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
  • Cannot resolve symbol ‘XXX‘ Maven 依赖问题的解决过程
  • SpringBoot统一数据返回格式 统一异常处理
  • 在Ubuntu上用Llama Factory命令行微调Qwen2.5的简单过程
  • 相互作用感知的蛋白-小分子对接模型 - Interformer 评测
  • Vue.js `setup()` 函数的使用
  • Vuex中的getter和mutation有什么区别
  • 团体程序设计天梯赛-练习集——L1-025 正整数A+B
  • AttributeError: can‘t set attribute ‘lines‘
  • 【Proteus仿真】【51单片机】多功能计算器系统设计
  • 力扣题目【6. Z 字形变换】 Java题解
  • SQL UCASE() 函数详解
  • 将DeepSeek接入Word,打造AI办公助手
  • Spring AI 在微服务中的应用:支持分布式 AI 推理
  • RK3568使用opencv(使用摄像头捕获图像数据显示)
  • python-decouple和 django-environ管理 Python/Django 项目中的环境变量
  • Van-Nav:新年,将自己学习的项目地址统一整理搭建自己的私人导航站,供自己后续查阅使用,做技术的同学应该都有一个自己网站的梦想
  • deepseek本地部署
  • 算法每日双题精讲 —— 前缀和(【模板】一维前缀和,【模板】二维前缀和)
  • 线性调整器——耗能型调整器
  • 练习题 - Django 4.x Auth 身份验证使用示例和配置方法
  • HTB:Cicada[RE-WriteUP]
  • 推荐七节来自NVIDIA、Google、斯坦福的AI课程
  • mysql.sock.lock 导致mysql重启失败
  • 《深度剖析Q-learning中的Q值:解锁智能决策的密码》