贪吃蛇(C++实现)
该贪吃蛇项目涉及到的C++知识点:
1. 面向对象编程
• 类与对象:定义了Point、Snake、Board、Game等类
• 封装:使用private保护内部状态,提供公共接口
• 构造函数:带默认参数的构造函数和初始化列表
• 枚举类:Direction、GameState、Difficulty等强类型枚举
2. 标准模板库(STL)
• 容器:vector(蛇身体)、string(字符串处理)
• 算法:find(查找坐标)、随机数生成(random_device, mt19937)
• 智能指针:未显式使用,但可改进内存管理
• 流处理:iostream、fstream(文件操作)
3. 操作符重载
• Point类重载==运算符用于坐标比较
4. 输入输出系统
• 控制台输入输出(iostream)
• 文件流操作(fstream)
5. 跨平台编程
• 预处理器指令(#ifdef _WIN32)
• 平台特定API(Windows API和Unix termios)
6. 时间处理
• chrono库用于时间控制和休眠
• system_clock获取系统时间
7. 多线程
• thread和this_thread用于休眠控制
8. 随机数生成
• 现代C++随机数库(random_device, mt19937, uniform_int_distribution)
9. 类型转换
• static_cast用于安全类型转换
10. 字符串处理
• string类和流操作(stringstream)
相关源代码:
#include <iostream>//标准输入输出对象
#include <vector>//提供动态数组功能
#include <cstdlib>//提供通用工具函数
#include <ctime>//提供时间处理功能
#include <conio.h> //Windows控制台输入
#include <fstream>//用于文件操作
#include <string>//提供字符串类型和操作
#include <algorithm>//提供通用算法
#include <thread>//多线程支持
#include <chrono>//提供现代化的时间处理
#include <random> // 需要包含随机数库/*** @file 跨平台控制台头文件包含** 根据目标平台智能包含控制台操作所需的头文件* 实现Windows和Unix-like系统的兼容支持*/// 如果目标平台是Windows(包括32位和64位)
#ifdef _WIN32/*** @brief 包含Windows API头文件** <windows.h> 提供Windows系统API,包括:* - 控制台操作函数(GetStdHandle, SetConsoleMode等)* - 输入输出控制* - 系统信息获取*/
#include <windows.h> // Windows控制台设置// 其他平台(Linux、macOS等Unix-like系统)
#else/*** @brief 包含Unix终端控制头文件** <termios.h> 提供终端I/O接口,包含:* - termios结构体(终端设置)* - tcgetattr/tcsetattr函数(获取/设置终端属性)*/
#include <termios.h>/*** @brief 包含Unix标准库头文件** <unistd.h> 提供POSIX操作系统API,包括:* - 文件描述符操作(STDIN_FILENO)* - read/write系统调用* - sleep/usleep函数*/
#include <unistd.h>
#endif//避免每次使用标准库组件时都要写 std:: 前缀
using namespace std;
using namespace std::chrono;// 点坐标结构体
struct Point {int x, y; // 1. 两个整型成员变量表示坐标Point(int x = 0, int y = 0) // 2. 带默认参数的构造函数: x(x), y(y) { // 使用成员初始化列表} // 3. 重载==运算符用于坐标比较//const Point& other 常引用方式传入另一个Point对象(避免拷贝)//括号后的 const 承诺不修改当前对象bool operator==(const Point& other) const {return x == other.x && y == other.y;}
};// 方向枚举
enum class Direction
{ UP, //上DOWN, //下LEFT, //左RIGHT //右
};// 游戏状态
enum class GameState
{ RUNNING, //运行PAUSED, //停止GAME_OVER, //结束MENU //菜单
};// 难度级别
enum class Difficulty
{ EASY, //容易MEDIUM, //中等HARD //困难
};// 蛇类
class Snake {
//私有
private:vector<Point> body; // 蛇身体Direction direction; // 当前方向bool growNextMove; // 下次移动是否增长public:Snake(Point start) {// 初始化蛇身(3个点)body.push_back(start);body.push_back(Point(start.x - 1, start.y));body.push_back(Point(start.x - 2, start.y));direction = Direction::RIGHT;//默认方向:向右growNextMove = false;//下次移动不增长}// 改变蛇的移动方向void changeDirection(Direction newDir) {/*** 防止180度转向检查:** 在贪吃蛇游戏中,不允许蛇进行180度的直接转向(即不允许蛇头直接调转方向),* 因为这样会导致蛇立即撞到自己的身体而游戏结束。** 检查逻辑:* 1. 如果当前方向是向上(UP),而新方向是向下(DOWN) → 禁止* 2. 如果当前方向是向下(DOWN),而新方向是向上(UP) → 禁止* 3. 如果当前方向是向左(LEFT),而新方向是向右(RIGHT) → 禁止* 4. 如果当前方向是向右(RIGHT),而新方向是向左(LEFT) → 禁止** 以上四种情况都表示玩家试图让蛇进行180度转向,这是不允许的。*/if ((direction == Direction::UP && newDir == Direction::DOWN) ||(direction == Direction::DOWN && newDir == Direction::UP) ||(direction == Direction::LEFT && newDir == Direction::RIGHT) ||(direction == Direction::RIGHT && newDir == Direction::LEFT)) {// 如果检测到180度转向请求,直接忽略并返回return;}// 如果新方向是有效的(非180度转向),更新蛇的移动方向direction = newDir;}// 移动蛇void move() {//获取蛇头位置Point newHead = getHead();/*(0,0)(1,0)...(0,1)|——————————x...||||y*/// 根据方向计算新头部位置switch (direction) { // 根据移动方向选择分支case Direction::UP: // 当方向为"上"时newHead.y--; // 向上移动:y坐标减1(屏幕坐标系中,y值减小向上移动)break; // 跳出switchcase Direction::DOWN: // 当方向为"下"时newHead.y++; // 向下移动:y坐标加1(y值增大向下移动)break; // 跳出switchcase Direction::LEFT: // 当方向为"左"时newHead.x--; // 向左移动:x坐标减1(x值减小向左移动)break; // 跳出switchcase Direction::RIGHT: // 当方向为"右"时newHead.x++; // 向右移动:x坐标加1(x值增大向右移动)break; // 跳出switch}//添加新头部//在最开始的地方插入新的头部位置body.insert(body.begin(), newHead);// 如果不需要增长,移除尾部if (!growNextMove) {// 移除尾部 → 总长度不变body.pop_back();}else {// 重置标志 → 下次移动恢复常态growNextMove = false;}}// 获取蛇头位置Point getHead() const {//返回vector<Point>的第一个元素return body.front();}// 获取蛇身//返回常量引用,避免拷贝且防止修改//成员函数不会修改对象状态const vector<Point>& getBody() const {//返回成员变量body的引用return body;}// 设置增长标志void grow() {growNextMove = true;}// 检查是否撞到自己bool checkSelfCollision() const {const Point& head = getHead();// 从第二个点开始检查(跳过头部)for (size_t i = 1; i < body.size(); i++) {if (head == body[i]) {return true;}}return false;}
};// 游戏板类
class Board {
//私有
private:int width, height;//宽和高Point food;//食物位置int score;//分数Difficulty difficulty;//难度int speed; // 移动速度(毫秒)
//公有
public://食物(字母)char letter;// 生成随机大写字母(A-Z)char generateRandomLetter() {/*** 使用现代C++随机数库生成高质量随机字母** 优点:* 1. 避免使用旧式rand(),提供更好的随机性* 2. 线程安全(静态变量初始化保证)* 3. 均匀分布保证每个字母概率相等*/// 静态随机设备对象(一次性初始化)// random_device: 硬件熵源,提供真正的随机种子// static: 保证只初始化一次,避免重复创建开销static std::random_device rd;/*** 静态Mersenne Twister随机数引擎** mt19937: 高性能随机数生成算法* 参数: rd() - 使用random_device生成种子* static: 保证引擎状态持续存在,避免重复初始化*/static std::mt19937 gen(rd());/*** 静态均匀分布对象** uniform_int_distribution: 生成均匀分布的整数* 参数:* 'A' - ASCII值65 (大写字母A)* 'Z' - ASCII值90 (大写字母Z)* static: 保持分布状态,提高性能*/static std::uniform_int_distribution<> dis('A', 'Z');/*** 生成并返回随机字母** 1. dis(gen): 使用引擎生成分布范围内的随机整数* 2. static_cast<char>: 将整数转换为对应ASCII字符*/return static_cast<char>(dis(gen));}/*** @brief Board 类构造函数** 初始化游戏板对象,设置基本属性并准备游戏状态** @param w 游戏板宽度(格子数)* @param h 游戏板高度(格子数)*/Board(int w, int h): width(w), // 初始化宽度height(h), // 初始化高度score(0) // 初始化分数为0{// 设置默认难度为中等setDifficulty(Difficulty::MEDIUM);// 生成初始食物位置generateFood();}/*** @brief 设置游戏难度级别** 根据指定的难度级别调整游戏的核心参数,主要是蛇的移动速度。* 难度越高,蛇移动速度越快,游戏挑战性越大。** @param diff 难度级别枚举值*/void setDifficulty(Difficulty diff) {// 保存难度设置到成员变量difficulty = diff;// 根据难度级别设置蛇的移动速度(毫秒/步)switch (difficulty) {case Difficulty::EASY: // 简单难度speed = 200; // 慢速(0.2秒/步)break;case Difficulty::MEDIUM: // 中等难度(默认)speed = 150; // 中速(0.15秒/步)break;case Difficulty::HARD: // 困难难度speed = 100; // 快速(0.1秒/步)break;}}// 获取难度Difficulty getDifficulty() const {return difficulty;}// 获取速度int getSpeed() const {return speed;}// 生成食物void generateFood() {// 随机位置生成食物//rand() % N:生成0到N-1的随机数//+1:将范围偏移到1到N/*实际生成范围假设游戏区域宽20高15:x坐标:1到 18(20 - 2)y坐标:1到 13(15 - 2)*/food.x = rand() % (width - 2) + 1;food.y = rand() % (height - 2) + 1;//生成的食物(字母)letter = generateRandomLetter();}// 获取食物位置Point getFood() const {return food;}/*** @brief 检查蛇是否吃到食物** 检测蛇头是否与食物位置重合,如果吃到食物则:* 1. 增加玩家分数(难度越高分数越高)* 2. 生成新的食物位置* 3. 返回检测结果** @param snake 蛇对象引用* @return true 如果蛇吃到食物* @return false 如果蛇未吃到食物*/bool checkFoodEaten(const Snake& snake) {// 检查蛇头位置是否与食物位置重合if (snake.getHead() == food) {/*** 计算并增加分数:* 基础分:10分* 难度加成:难度值 × 10** 难度枚举值:* EASY = 0* MEDIUM = 1* HARD = 2** 分数示例:* 简单:0×10 + 10 = 10分* 中等:1×10 + 10 = 20分* 困难:2×10 + 10 = 30分*/score += static_cast<int>(difficulty) * 10 + 10;// 生成新的食物位置generateFood();// 返回吃到食物的结果return true;}// 未吃到食物return false;}/*** @brief 检查蛇是否撞墙(边界碰撞检测)** 检测蛇头是否与游戏板边界发生碰撞,即蛇头是否超出游戏区域的有效范围** @param snake 蛇对象引用* @return true 如果蛇头撞到边界* @return false 如果蛇头在安全区域内*/bool checkWallCollision(const Snake& snake) const {// 获取蛇头当前位置const Point& head = snake.getHead();/*** 检测蛇头是否在边界上或超出边界:* 游戏板边界范围:* x轴:0 到 width-1* y轴:0 到 height-1** 有效游戏区域(内部区域):* x: 1 到 width-2* y: 1 到 height-2** 碰撞条件:* 1. 蛇头x坐标 <= 0 (左边界)* 2. 蛇头x坐标 >= width-1 (右边界)* 3. 蛇头y坐标 <= 0 (上边界)* 4. 蛇头y坐标 >= height-1 (下边界)*/return head.x <= 0 || // 左边界碰撞head.x >= width - 1 || // 右边界碰撞head.y <= 0 || // 上边界碰撞head.y >= height - 1; // 下边界碰撞}// 增加分数void addScore(int points) {score += points;}// 获取分数int getScore() const {return score;}// 获取宽度int getWidth() const {return width;}// 获取高度int getHeight() const {return height;}
};// 游戏类
class Game {//私有
private://游戏板类Board board;//贪吃蛇Snake snake;//游戏状态GameState state;//是否是 停止请求bool quitRequested;//分数int highScore;/*** @brief 保存最高分到文件** 将当前最高分记录写入文本文件,以便在程序重启后恢复* 文件格式:纯文本整数*/void saveHighScore() {// 创建输出文件流对象,指定文件名ofstream file("highscore.txt");// 检查文件是否成功打开if (file.is_open()) {// 将最高分写入文件file << highScore;// 关闭文件流file.close();}// 如果文件打开失败,不做处理(可添加错误日志)}/*** @brief 从文件加载最高分** 从文本文件中读取历史最高分记录* 如果文件不存在或读取失败,将最高分设为0*/void loadHighScore() {// 创建输入文件流对象,指定文件名ifstream file("highscore.txt");// 检查文件是否成功打开if (file.is_open()) {// 从文件读取最高分file >> highScore;// 关闭文件流file.close();}else {// 文件不存在或无法打开时,重置最高分为0highScore = 0;}}//公有
public:// Game 类构造函数Game(int width = 20, int height = 15) // 参数带默认值: board(width, height), // 初始化列表开始snake(Point(width / 2, height / 2)), // 初始化蛇在中心位置state(GameState::MENU), // 初始状态为菜单quitRequested(false) // 退出标志设为false{// 构造函数体srand(static_cast<unsigned int>(time(nullptr))); // 初始化随机种子loadHighScore(); // 加载历史最高分}/*** @brief 处理游戏输入** 根据当前游戏状态处理键盘输入,控制游戏流程和蛇的移动** 使用 _kbhit() 和 _getch() 实现非阻塞键盘输入检测*/void processInput() {// 检查是否有按键按下(非阻塞)if (_kbhit()) {// 获取按下的键(不显示在控制台)char key = _getch();// 根据当前游戏状态处理按键switch (state) {/************************** 游戏运行状态处理*************************/case GameState::RUNNING:switch (key) {case 'w': // 向上移动snake.changeDirection(Direction::UP);break;case 's': // 向下移动snake.changeDirection(Direction::DOWN);break;case 'a': // 向左移动snake.changeDirection(Direction::LEFT);break;case 'd': // 向右移动snake.changeDirection(Direction::RIGHT);break;case 'p': // 暂停游戏state = GameState::PAUSED;break;case 'q': // 退出游戏quitRequested = true;break;}break; // 结束 RUNNING 状态处理/************************** 游戏暂停状态处理*************************/case GameState::PAUSED:if (key == 'p') {// 恢复游戏state = GameState::RUNNING;}else if (key == 'q') {// 退出游戏quitRequested = true;}break; // 结束 PAUSED 状态处理/************************** 游戏结束状态处理*************************/case GameState::GAME_OVER:if (key == 'r') {// 重新开始游戏resetGame();}else if (key == 'q') {// 退出游戏quitRequested = true;}break; // 结束 GAME_OVER 状态处理/************************** 主菜单状态处理*************************/case GameState::MENU:switch (key) {case '1': // 选择简单难度board.setDifficulty(Difficulty::EASY);state = GameState::RUNNING; // 进入游戏break;case '2': // 选择中等难度board.setDifficulty(Difficulty::MEDIUM);state = GameState::RUNNING; // 进入游戏break;case '3': // 选择困难难度board.setDifficulty(Difficulty::HARD);state = GameState::RUNNING; // 进入游戏break;case 'h': // 查看高分榜showHighScores();break;case 'q': // 退出游戏quitRequested = true;break;}break; // 结束 MENU 状态处理} // 结束状态选择 switch} // 结束按键检查 if}// 更新游戏状态void update() {//如果不是 运行状态,直接返回if (state != GameState::RUNNING) return;//运行状态,移动蛇snake.move();// 检查是否吃到食物if (board.checkFoodEaten(snake)) {//吃到食物后,贪吃蛇增长snake.grow();}// 检查碰撞//如果撞墙 或者 撞到自己 if (board.checkWallCollision(snake) || snake.checkSelfCollision()) {//游戏标志 设置为:结束 标志state = GameState::GAME_OVER;//如果 当前得分 大于 历史最高得分if (board.getScore() > highScore) {//更新 历史最高得分为 当前得分highScore = board.getScore();//保存高分到文件saveHighScore();}}}/*** @brief 渲染游戏界面** 根据当前游戏状态渲染不同的游戏界面,包括:* - 游戏运行/暂停界面* - 游戏结束界面* - 主菜单界面** 每次调用都会先清屏,然后渲染对应状态的界面*/void render() {// 清空控制台屏幕,准备绘制新帧system("cls"); // Windows系统清屏命令// 只在状态变化时清屏/* static GameState lastState = GameState::MENU;if (state != lastState) {system("cls");lastState = state;}*/// 根据当前游戏状态选择渲染内容switch (state) {case GameState::RUNNING: // 游戏运行状态case GameState::PAUSED: // 游戏暂停状态/*** 渲染游戏主界面:* - 游戏板* - 蛇身* - 食物* - 分数显示** 注意:RUNNING和PAUSED状态共享同一基础界面* 在renderGame()内部会区分显示暂停状态*/renderGame();break;case GameState::GAME_OVER: // 游戏结束状态/*** 渲染游戏结束界面:* - 显示最终得分* - 显示最高分* - 提供重新开始或退出选项*/renderGameOver();break;case GameState::MENU: // 主菜单状态/*** 渲染主菜单界面:* - 游戏标题* - 难度选择选项* - 高分榜查看* - 退出游戏选项*/renderMenu();break;}}/*** 渲染游戏主界面:* - 游戏板* - 蛇身* - 食物* - 分数显示** 注意:RUNNING和PAUSED状态共享同一基础界面* 在renderGame()内部会区分显示暂停状态*/void renderGame() {// 绘制顶部边框cout << "分数: " << board.getScore() << " | 最高得分: " << highScore;if (state == GameState::PAUSED) {cout << " | 暂停";}cout << "\n";for (int i = 0; i < board.getWidth(); i++) {cout << "#";}cout << "\n";// 绘制游戏区域for (int y = 0; y < board.getHeight(); y++) {for (int x = 0; x < board.getWidth(); x++) {Point current(x, y);// 绘制边框if (x == 0 || x == board.getWidth() - 1 || y == 0 || y == board.getHeight() - 1) {cout << "#";}// 绘制食物else if (current == board.getFood()) {cout << board.letter;}// 绘制蛇else {bool isSnakePart = false;const auto& body = snake.getBody();// 检查是否是蛇头if (current == body.front()) {cout << "O";isSnakePart = true;}// 检查是否是蛇身else {for (size_t i = 1; i < body.size(); i++) {if (current == body[i]) {cout << "o";isSnakePart = true;break;}}}// 空位置if (!isSnakePart) {cout << " ";}}}cout << "\n";}// 绘制底部边框for (int i = 0; i < board.getWidth(); i++) {cout << "#";}cout << "\n";// 控制说明cout << "控制: W=上, S=下, A=左, D=右\n";cout << "P=暂停游戏, Q=停止游戏\n";}// 渲染游戏结束界面void renderGameOver() {cout << "===== 游戏结束=====\n\n";cout << "最终得分: " << board.getScore() << "\n";cout << "最高得分: " << highScore << "\n\n";cout << "按 'R' 重新开始游戏\n";cout << "按'Q' 停止游戏\n";}// 渲染主菜单void renderMenu() {cout << "=====贪吃蛇游戏 =====\n\n";cout << "1. 容易\n";cout << "2. 中等\n";cout << "3. 困难\n";cout << "H. 查看高分\n";cout << "Q. 停止游戏\n";cout << "请选择: ";}// 显示高分void showHighScores() {//用于清空控制台屏幕内容system("cls");cout << "=====分数 =====\n\n";cout << "当前最高得分: " << highScore << "\n\n";cout << "按任意键返回...";_getch(); // 等待按键}// 重置游戏void resetGame() {snake = Snake(Point(board.getWidth() / 2, board.getHeight() / 2));board = Board(board.getWidth(), board.getHeight());//:: 是 C++ 的作用域解析运算符state = GameState::RUNNING;}// 运行游戏void run() {while (!quitRequested) {processInput();//:: 是 C++ 的作用域解析运算符if (state == GameState::RUNNING) {update();}render();// 控制游戏速度//使当前线程暂停执行指定时长//不消耗CPU资源(操作系统级休眠)//常用于控制游戏循环的帧率 / 速度//通常返回数值表示毫秒间隔this_thread::sleep_for(milliseconds(board.getSpeed()));}}// 获取游戏状态GameState getState() const {return state;}
};// 设置控制台为非阻塞输入模式
/*** @brief 配置控制台输入模式** 此函数用于设置控制台输入模式,实现:* 1. 非阻塞输入:无需按回车即可读取按键* 2. 无回显输入:按键不在控制台显示** 根据操作系统自动选择Windows或Unix-like系统的实现方式*/
void setupConsole() {// Windows平台专用设置
#ifdef _WIN32// 获取标准输入句柄(控制台输入)HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);// 用于存储当前控制台模式DWORD mode;// 获取当前控制台模式GetConsoleMode(hStdin, &mode);/*** 设置新的控制台模式:* 1. ~ENABLE_ECHO_INPUT: 禁用输入回显(按键不在控制台显示)* 2. ~ENABLE_LINE_INPUT: 禁用行输入模式(无需按回车即可读取输入)** 使用位运算清除对应标志位:* mode & (~ENABLE_ECHO_INPUT) → 清除回显标志* mode & (~ENABLE_LINE_INPUT) → 清除行输入标志*/SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT) & (~ENABLE_LINE_INPUT));// Unix-like系统(Linux/macOS)专用设置
#else// 存储原始终端属性(用于恢复)termios oldt;// 获取当前终端属性(STDIN_FILENO = 标准输入文件描述符)tcgetattr(STDIN_FILENO, &oldt);// 创建终端属性副本用于修改termios newt = oldt;/*** 修改终端标志位:* 1. ~ICANON: 禁用规范模式(即禁用行缓冲,实现实时输入)* 2. ~ECHO: 禁用输入回显(按键不在终端显示)** 使用位运算清除对应标志位:* newt.c_lflag &= ~(ICANON | ECHO)*/newt.c_lflag &= ~(ICANON | ECHO);/*** 应用新的终端属性:* 1. STDIN_FILENO: 标准输入文件描述符* 2. TCSANOW: 立即应用更改(无需等待数据发送完成)*/tcsetattr(STDIN_FILENO, TCSANOW, &newt);
#endif
}// 恢复控制台设置
/*** @brief 恢复控制台原始设置** 此函数用于恢复控制台到调用 setupConsole() 之前的状态* 应与 setupConsole() 配对使用,确保程序退出后控制台行为正常*/
void restoreConsole() {// Windows平台恢复设置
#ifdef _WIN32// 获取标准输入句柄(控制台输入)HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);// 用于存储当前控制台模式DWORD mode;// 获取当前控制台模式GetConsoleMode(hStdin, &mode);/*** 恢复原始控制台模式:* 1. ENABLE_ECHO_INPUT: 重新启用输入回显(按键在控制台显示)* 2. ENABLE_LINE_INPUT: 重新启用行输入模式(需要按回车才能读取输入)** 使用位运算设置对应标志位:* mode | ENABLE_ECHO_INPUT → 设置回显标志* mode | ENABLE_LINE_INPUT → 设置行输入标志*/SetConsoleMode(hStdin, mode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);// Unix-like系统(Linux/macOS)恢复设置
#else// 声明存储原始终端属性的变量// 注意:这里假设在setupConsole()中已经保存了原始属性// 实际实现中,oldt应该在setupConsole()中保存并在全局可访问termios oldt;// 在实际应用中,oldt应该是一个全局变量或在setupConsole()中保存// 这里仅为示意,实际代码需要确保oldt已正确保存/*** 恢复原始终端属性:* 1. STDIN_FILENO: 标准输入文件描述符* 2. TCSANOW: 立即应用更改* 3. &oldt: 使用之前保存的原始属性*/tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#endif
}//主函数
int main() {//设置控制台为非阻塞模式/* 在贪吃蛇游戏中,非阻塞输入非常重要,因为游戏需要实时更新(例如蛇的移动),而不是等待用户输入。如果使用阻塞输入,游戏会停顿直到玩家按键,这会导致游戏体验不流畅。*/setupConsole(); //在C++中,创建对象有两种主要方式:栈上分配和堆上分配//栈上分配:直接声明//堆上分配:使用new//栈分配对象Game game;game.run();//恢复控制台设置restoreConsole(); return 0;
}