从C转C++:智能指针与引用理解及Game类代码实现疑问
嘿,从C转C++的过程中啃透智能指针和引用确实得踩几个小坑,不过你的需求逻辑很清晰,我来一步步帮你实现:
核心思路拆解
你的目标是让每个GamePlayer持有自己的GamePiece集合,然后把这些棋子的引用挂载到Game类的_board(reference_wrapper<GamePiece>二维数组)里。关键要注意引用的生命周期安全——reference_wrapper只是引用的包装,它不管理对象内存,必须保证它指向的GamePiece在_board的整个生命周期内都存在。
具体实现步骤
1. 先补全基础类定义
假设你还没明确GamePiece的结构,先写个极简版本做示例:
#include <vector> #include <functional> // 用于reference_wrapper #include <stdexcept> // 用于异常处理 class GamePiece { public: // 示例成员,你可以根据需求扩展 int id = 0; bool isActive = true; GamePiece() = default; GamePiece(int pieceId) : id(pieceId) {} };
2. 修改GamePlayer类,让它持有棋子并提供引用访问
给GamePlayer添加vector<GamePiece>成员,同时提供安全的引用获取方法(一定要加边界检查,避免越界访问导致未定义行为):
class GamePlayer { private: std::vector<GamePiece> _ownedPieces; // 玩家持有的棋子集合 public: // 构造函数:初始化玩家的棋子数量 GamePlayer(int initialPieceCount) { _ownedPieces.reserve(initialPieceCount); for (int i = 0; i < initialPieceCount; ++i) { _ownedPieces.emplace_back(i); // 用id初始化棋子 } } // 获取指定索引的棋子引用 GamePiece& getPiece(int index) { if (index < 0 || index >= _ownedPieces.size()) { throw std::out_of_range("GamePlayer: 棋子索引越界"); } return _ownedPieces[index]; } // 可选:返回整个棋子集合的const引用,方便外部遍历 const std::vector<GamePiece>& getAllPieces() const { return _ownedPieces; } };
3. 调整Game类,将玩家的棋子引用填充到_board
Game类的_players是成员变量,只要Game对象存活,_players里的GamePlayer和他们的_ownedPieces就会一直存在,这样挂载到_board的引用就不会悬垂。我们在Game的构造函数里完成_board的填充:
// 先定义编译期常量(你可以根据自己的需求修改) constexpr int BOARD_ROWS = 3; constexpr int BOARD_COLS = 3; constexpr int PLAYER_COUNT = 2; constexpr int PIECES_PER_PLAYER = 3; class Game { public: Game() { // 初始化玩家:每个玩家持有PIECES_PER_PLAYER个棋子 _players.reserve(PLAYER_COUNT); for (int i = 0; i < PLAYER_COUNT; ++i) { _players.emplace_back(PIECES_PER_PLAYER); } // 将玩家的棋子引用填充到_board(这里的填充逻辑你可以自定义) int pieceCounter = 0; for (int row = 0; row < BOARD_ROWS; ++row) { for (int col = 0; col < BOARD_COLS; ++col) { // 前PLAYER_COUNT*PIECES_PER_PLAYER个位置放玩家的棋子,剩余位置留空 if (pieceCounter < PLAYER_COUNT * PIECES_PER_PLAYER) { int playerIdx = pieceCounter / PIECES_PER_PLAYER; int pieceIdx = pieceCounter % PIECES_PER_PLAYER; _board[row][col] = std::ref(_players[playerIdx].getPiece(pieceIdx)); } // 超出范围的位置保持默认的EmptyPiece引用 pieceCounter++; } } } private: static GamePiece EmptyPiece; // 空棋子静态实例 std::reference_wrapper<GamePiece> _board[BOARD_ROWS][BOARD_COLS] = {std::ref(Game::EmptyPiece)}; std::vector<GamePlayer> _players; }; // 静态成员变量必须在类外初始化 GamePiece Game::EmptyPiece;
关键注意事项
绝对不要让引用悬垂:
- 不要在
GamePlayer的_ownedPieces中删除元素(比如调用erase、pop_back),否则对应的reference_wrapper会指向已销毁的对象,访问时触发未定义行为。 - 不要让
GamePlayer对象被提前销毁(比如从_players中移除),同理会导致引用失效。
- 不要在
如果需要动态增减棋子:
如果你的游戏逻辑需要玩家动态添加/删除棋子,vector<GamePiece>的扩容会导致元素地址变化,原来的引用会失效。这种情况下建议改用std::shared_ptr<GamePiece>:- 让
GamePlayer持有std::vector<std::shared_ptr<GamePiece>> Game的_board改成std::shared_ptr<GamePiece>类型,这样即使vector扩容,指针依然有效(因为棋子对象在堆上)。
- 让
内容的提问来源于stack exchange,提问作者galah92
相关产品推荐
相关产品推荐

