我正在设计一款简单的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
和{{{ 1}})。
再次单步进入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();
的{{1}}只有red() == rhs.red()
。这是程序崩溃的关键点。调试器声明red()
(return _red;
)为0x0。
我对于为什么会发生这种情况一无所知。我最好的猜测是我错误地使用了智能指针。我之前从未真正使用过它,但它应该与普通指针非常相似,如果指针是this
,我认为this->_red
不会完成任何事情。
release
成为0x0的原因是什么?
修改
我确定所有内容都已初始化,正如我在每个构造函数中所做的那样,在成员初始值设定项中(例如nullptr
),其中NONE是透明的黑色。
我也不太熟悉调试器,因为我没有那么多地使用它来打印调试值。
答案 0 :(得分:3)
该行
*_owner = owner;
表示“制作owner
对象的副本,并将其存储在_owner
指向的位置。”问题是_owner
还没有指出任何东西;它仍然是空的。
如果您真的想在播放器控制的每个磁贴中制作Player
对象的副本,则需要执行
_owner.reset(new Player(owner));
但制作Player
对象的副本是一件奇怪的事情。请考虑使用shared_ptr
- 您可以将owner
和_owner
都设为shared_ptr
,并按照通常的方式将其中一个分配给另一个。
答案 1 :(得分:1)
您从默认初始化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))
。您是否只希望每个磁贴都可以引用已有的播放器?你能保证Player对象owner
比Board :: Tile对象更长吗?然后你想要一个原始指针:(在Board :: Tile的声明中)Player const *_owner;
(在实现中)_owner=&owner;
。