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

【C++】 类练习---封装链表、人物移动

目录

前言

正文

结构体和类的区别

练习1:封装链表

用类封装链表的注意事项

定义一个链表的节点结构

链表类代码

主函数

运行结果

练习2:人物移动

说明 

头文件以及宏

人物类

主函数

运行结果

结语


前言

在学完了【C++】 类基础汇总(类封装,构造、析构函数...)后,我们明白了类的特性以及使用方法,那么就需要用一些实例来巩固一下知识点,所以这篇文章就教你如何用类来封装链表和用类来写一个人物移动的小项目。


正文

结构体和类的区别

C++中的结构体和类很像,可以有构造-析构,也有访问修饰符,继承、多态等。区别有两点:

  1. 类成员属性:方法默认是私有的(private),而结构体默认是公有的(public)。
  2. 当从基类、结构体中继承时,类的默认继承方式是私有的(private),而结构体是公有的(public)。

此外在使用习惯上看也略有差别:一般情况,使用类来描述功能实现,而结构体通常只是纯粹的表示数据。比如链表有复杂的数据、很多的方法一般定义成类,而节点只是描述了持有数据的类型和指向下一个节点的指针,一般被定义为结构体。

练习1:封装链表

用类封装链表的注意事项

在使用类封装链表时,需要注意以下几点:

  1. 将链表的节点定义为类中的一个私有内部类,这个内部类应该至少包含两个成员变量:当前节点的值和指向下一个节点的指针。

  2. 类中应该包含头节点和尾节点的指针。头节点是第一个有效节点之前的虚拟节点,尾节点是最后一个有效节点。

  3. 需要实现一些基本的方法,如增加节点、删除节点、查找节点、获取链表长度、判断链表是否为空等。这些方法应该被定义为公有成员函数,以方便外部调用。

  4. 在进行插入或删除操作时,需要注意更新头节点和尾节点的指针。如果链表为空,则插入第一个节点时应同时将头节点和尾节点指向新插入的节点。

  5. 要防止内存泄漏问题,需要在析构函数中对链表中的所有节点进行释放。

定义一个链表的节点结构

struct Node {
	int val;
	Node* pNext;
	Node(int v) {//构造函数初始化
		val = v;
		pNext = nullptr;
	}
};

链表类代码

class CList {
public:
	Node* m_pHead;
	Node* m_pEnd;
	int m_nLen;

public:
	CList() {
		m_pHead = m_pHead = nullptr;
		m_nLen = 0;
	}
	~CList() {
		Node* pNode = nullptr;
		while (m_pHead) { //如果链表不为空,循环
			pNode = m_pHead;  //标记头

			m_pHead = m_pHead->pNext; //头向后移动
			delete pNode; //删除标记的
		}
		Node* m_pHead = nullptr;
		Node* m_pEnd = nullptr;
		int m_nLen = 0;
	}

	void PushBack(int v) {
		Node* pNode = new Node(v);
		if (m_pHead) { //非空链表
			m_pEnd->pNext = pNode;
			m_pEnd = pNode;
		}
		else {//空链表
			m_pHead = pNode;
		}
		m_pEnd = pNode;
		m_nLen++;
	}

	void PopFront() {
		if (m_pHead) {
			Node* pNode = m_pHead;  //标记头,也是将来要删除的
			if (m_pHead == m_pEnd) {  //1个节点
				m_pHead = m_pEnd = nullptr;
			}
			else {  //多个节点
				m_pHead = m_pHead->pNext;//向后移动
			}
			delete pNode; //删除标记的
			pNode = nullptr;
			m_nLen--;
		}
	}

	void ShowList() {
		Node* pNode = m_pHead;
		while (pNode) {
			cout << pNode->val << "  ";
			pNode = pNode->pNext;//向后移动
		}
		cout << endl;
	}

	int GetLength() {
		return m_nLen;
	}

};

主函数

int main()
{
	CList lst;
	lst.PushBack(1);
	lst.PushBack(2);
	lst.PushBack(3);
	lst.PushBack(4);

	cout << lst.GetLength() << endl;

	lst.ShowList();

	lst.PopFront();
	lst.PopFront();

	cout << lst.GetLength() << endl;
	lst.ShowList();

	return 0;
}

运行结果

练习2:人物移动

说明 

用类封装一下人物,其中类成员属性包含人物的图片、坐标和方向;类成员函数包含人物展示,人物移动和运行函数。

头文件以及宏

#include<iostream>
using namespace std;
#include<easyx.h>
#include<conio.h>

#define DIRECT_UP     72
#define DIRECT_DOWN   80
#define DIRECT_LEFT   75
#define DIRECT_RIGHT  77

#define MOVE_STEP  4

#define KEY_ESC 27

人物类

class CPeople {
public:
	IMAGE m_imgUp;     //图片
	IMAGE m_imgDown;
	IMAGE m_imgLeft;
	IMAGE m_imgRight;

	int m_x;    //坐标
	int m_y;


	int m_direct;    //方向
public:
	CPeople() {
		//约定:窗口大小 600*600
		//创建指定大小的窗口
		::initgraph(600, 600);

		//设定窗口的背景色,为白色
		::setbkcolor(RGB(255,255,255));
		::cleardevice();//使窗口背景色立即刷新



		m_x = 300;
		m_y = 300;

		m_direct = DIRECT_RIGHT;  //初始方向为右
		//图片变量和图片资源进行绑定关联
		::loadimage(&m_imgUp, L".\\res\\mali-up.bmp");
		::loadimage(&m_imgDown, L".\\res\\mali-down.bmp");
		::loadimage(&m_imgLeft, L".\\res\\mali-left.bmp");
		::loadimage(&m_imgRight, L".\\res\\mali-right.bmp");

	}

	~CPeople() {
		//关闭窗口
		::closegraph();
	}

public:
	void ShowPeople() {
		::BeginBatchDraw(); //批量绘图

		::cleardevice();//清除上一次绘图痕迹



		//-这是我的绘图代码--------------------------------------
		if (m_direct == DIRECT_UP) ::putimage(m_x, m_y, &m_imgUp);
		if (m_direct == DIRECT_DOWN) ::putimage(m_x, m_y, &m_imgDown);
		if (m_direct == DIRECT_LEFT) ::putimage(m_x, m_y, &m_imgLeft);
		if (m_direct == DIRECT_RIGHT) ::putimage(m_x, m_y, &m_imgRight);


		//---------------------------------------
		::EndBatchDraw();

	}

	void MovePeople(int direct,int step) {
		switch (direct)
		{
		case DIRECT_UP:
		{

			m_y - step >= 0 ? m_y -= step : m_y = 0;
			/*if (m_y - step >= 0) {
				m_y -= step;
			}
			else {
				m_y = 0;
			}*/
			m_direct = DIRECT_UP;
		}
		break;
		case DIRECT_DOWN:
		{
			if (m_y + step <= 540) {
				m_y += step;
			}
			else {
				m_y = 540;
			}
			m_direct = DIRECT_DOWN;
		}
		break;
		case DIRECT_LEFT:
		{
			if (m_x - step >= 0) {
				m_x -= step;
			}
			else {
				m_x = 0;
			}
			m_direct = DIRECT_LEFT;
		}
		break;
		case DIRECT_RIGHT:
		{
			if (m_x + step <= 540) {
				m_x += step;
			}
			else {
				m_x = 540;
			}
			m_direct = DIRECT_RIGHT;
		}
		break;

		}
	}

	void Run() {
		ShowPeople();
		while (1) {
			int key = _getch();//获取按下的字符码

			if (key == KEY_ESC) {//如果是退出,则break

				int ret = ::MessageBox(NULL, L"是否退出", L"提示", MB_OKCANCEL);
				if (ret == IDOK) {//确定
					break;
				}
				
			}

			//移动
			MovePeople(key, MOVE_STEP);

			//显示
			ShowPeople();

		}



	}

};

主函数

int main()
{
	CPeople peo;
	peo.Run();

	return 0;
}

运行结果


结语

以上就是两个用类来实现的小练习了,相信在学完之后你对类会有更深层次的理解,也能够明白类的重要性了。


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

相关文章:

  • 【OpenEuler】配置虚拟ip
  • ESLint 使用教程(五):ESLint 和 Prettier 的结合使用与冲突解决
  • 简易入手《SOM神经网络》的本质与原理
  • Oracle ADB 导入 BANK_GRAPH 的学习数据
  • 【excel】easy excel如何导出动态列
  • RabbitMQ高效的消息队列中间件原理及实践
  • gitlab使用docker简单快速部署
  • 数字座舱带动液晶仪表升级,哪些企业「领跑」前装量产份额
  • 20. 资源的调度——Node 亲和性(Node Affinity)
  • 亚马逊选品有什么技巧?品选对了可以带来什么好处?
  • 【图像分割】视觉大模型SEEM(Segment Everything Everywhere All at Once)原理解读
  • 登顶Nature 正刊!百度生物计算用AI首次实现mRNA领域重大进展
  • OpenFeign服务接口调用
  • 【Admin后台管理】Geodjango后台显示地图并加载空间字段
  • 计算机智能系统有哪些SCI期刊? - 易智编译EaseEditing
  • React实战模版
  • 【P6】JMeter HTTP Cookie管理器
  • 【算法导论】算法分析与设计_理论知识点(可用于备考)
  • 玩机搞机--定制系统 编译系统选项 隐藏设置 关闭app联网 增加设置选项
  • 现在的00后,真是卷死了呀,想离职了·····
  • SpringBoot的创建和使用
  • ios app真机测试到上架App Store详细教程-必看
  • Leetcode刷题之复制带随机指针的链表
  • 无线之红外线技术的组网方式详解
  • 【lambda表达式传值问题研究】
  • node.js+vue鲜花销售网站