为什么`this`等于0x0,导致程序崩溃?

时间:2012-04-26 01:51:08

标签: c++ segmentation-fault this smart-pointers

我正在设计一款简单的Connect 4游戏。到目前为止,我有4个基础类:

Colour - 负责表示颜色(RGBA)。包括转换运算符。

Player - 代表游戏的玩家。每个Player都有一个Colour和一个名称。

Board - 代表游戏板。它包含尺寸,以及具有这些尺寸的Tile s的2D矢量。

Tile - Board中的嵌套类。表示板上的一个空间。每个Tile对该图块的所有者都有Colourstd::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是透明的黑色。

我也不太熟悉调试器,因为我没有那么多地使用它来打印调试值。

2 个答案:

答案 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;