我正在设计一个简单的Connect 4游戏。到目前为止,我有 4 个基础类:
Colour
- 负责表示颜色 (RGBA)。包括转换运算符。
Player
- 代表游戏的玩家。每个Player
都有一个Colour
和一个名称。
Board
- 代表游戏板。它包含尺寸,以及具有这些尺寸的Tile
s的2D矢量。
Tile
- Board
中的嵌套类。表示板上的一个空格。每个Tile
都有一个Colour
,并且与该磁贴的所有者std::unique_ptr
。所有者从nullptr
开始,可以更改为Player
一次。颜色从透明的黑色开始。
我已经测试了我的Colour
类,它似乎工作正常。我的Player
课也处于最佳状态。但是,我在Board/Tile
类方面遇到了一些问题。
我的测试包括创建两个玩家和一个棋盘。这些正常执行。接下来,我遍历电路板的尺寸,每个瓷砖一次。然后我打电话
board.tile (j, i).claimBy (p2);
循环遍历带有i
的行和带有j
的列,这是您希望打印它的方式。
tile (j, i)
检索我正在使用的磁贴。它按预期工作。
导致崩盘的一系列事件:
claimBy (p2)
将图块设置为由玩家 2 认领。它的实现方式如下:
bool Board::Tile::claimBy (const Player &owner)
{
if (!_owner)
{
*_owner = owner;
_colour = owner.colour();
return true;
}
return false;
}
_owner
是我的std::unique_ptr<Player>
.它首先检查磁贴的所有者之前是否被设置过(即未nullptr
)。如果没有,它将里面的Player
设置为传入的那个。然后,它会更新磁贴的颜色并返回true
。如果之前已声明磁贴,则返回false
。
在调试器之后,崩溃发生在行*_owner = owner;
中。介入将我带到第 struct Player
行(我对 Player
类的声明),我将其视为隐式复制构造函数(请记住,该类只有一个Colour _colour
和一个std::string _name
)。
再次介入会导致我进入Colour::operator=
(这对于调用复制构造函数是有意义的)。定义如下:
Colour &Colour::operator= (const Colour &rhs)
{
if (*this != rhs)
{
_red = rhs.red();
_green = rhs.green();
_blue = rhs.blue();
_alpha = rhs.alpha();
}
return *this;
}
路径变成*this != rhs
.这只是对 operator==
的反向调用,即:
return red() == rhs.red()
&& green() == rhs.green()
&& blue() == rhs.blue()
&& alpha() == rhs.alpha();
这里的第一个比较red() == rhs.red()
red()
这只是return _red;
。这是程序崩溃的点。调试器声明this
(this->_red
)是0x0。
我不知道为什么会发生这种情况。我最好的猜测是我错误地使用了智能指针。我以前从未真正使用过,但它应该与普通指针非常相似,而且我认为如果指针nullptr
release
不会完成任何事情。
this
被0x0的原因可能是什么?
编辑:
我确定一切都被初始化了,因为我在每个构造函数中都这样做了,在成员初始值设定项中(例如 Board::Tile::Tile() : _colour (Colours::NONE), _owner (nullptr){}
),其中 NONE 是透明的黑色。
我也不太精通调试器,因为我在打印调试值时没有经常使用它。
行
*_owner = owner;
意思是"复制owner
对象,并将其存储在_owner
指向的位置。 问题是_owner
还没有指向任何东西;它仍然为空。
如果您确实想在玩家控制的每个磁贴中复制Player
对象,则需要这样做
_owner.reset(new Player(owner));
但是复制Player
对象是一件奇怪的事情。 考虑改用shared_ptr
- 您可以让owner
和_owner
都shared_ptr
,并以通常的方式将一个分配给另一个。
您从默认初始化的std::unique_ptr<Player>
开始。 也就是说,相当于具有一些清理语义的 NULL 指针。 然后,您尝试在语句*_owner=owner;
中取消引用它,以便可以分配给它。
因此语句*_owner=owner;
基本上等价于((Player*)NULL)->operator=(owner);
,调用隐式赋值运算符。 这做的第一件事相当于((Player*)NULL)->_colour=owner._colour;
在这里找到this==NULL
并不奇怪;事实上,这是意料之中的。
修复取决于您实际想要发生的情况。 是否应该给每个Board::Tile
一份全新的owner
副本? 然后你想改为说_owner.reset(new Player(owner))
. 你只希望每个图块都包含对现有玩家的引用吗? 你能保证玩家对象owner
会比 Board::Tile 对象的寿命长吗?然后你需要一个原始指针:(在 Board::Tile 的声明中)Player const *_owner;
(在实现中)_owner=&owner;
。