You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从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;

关键注意事项
  1. 绝对不要让引用悬垂:

    • 不要在GamePlayer的_ownedPieces中删除元素(比如调用erase、pop_back),否则对应的reference_wrapper会指向已销毁的对象,访问时触发未定义行为。
    • 不要让GamePlayer对象被提前销毁(比如从_players中移除),同理会导致引用失效。
  2. 如果需要动态增减棋子:
    如果你的游戏逻辑需要玩家动态添加/删除棋子,vector<GamePiece>的扩容会导致元素地址变化,原来的引用会失效。这种情况下建议改用std::shared_ptr<GamePiece>:

    • 让GamePlayer持有std::vector<std::shared_ptr<GamePiece>>
    • Game的_board改成std::shared_ptr<GamePiece>类型,这样即使vector扩容,指针依然有效(因为棋子对象在堆上)。

内容的提问来源于stack exchange,提问作者galah92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:53:37