为什么我会收到"访问冲突"在这个C ++代码中

时间:2016-12-09 16:19:16

标签: c++ c++11

我有以下三个类:

class BeliefRoot
{
    std::string m_Name;
    BeliefRoot(std::string Name) : m_Name(Name)
    { }
};

template <class factType>
class Belief : public BeliefRoot
{
    factType m_Fact;
    explicit Belief(std::string UniqueName, factType InitialFact = NULL)
                                            : BeliefRoot(UniqueName), m_Fact(InitialFact)
    { }
};

template <class factType>
class BeliefSet : public Belief<factType>
{
    std::list<factType> m_Facts;
    explicit BeliefSet(std::string UniqueName) : Belief(UniqueName)
    { }
};

现在我想要实例化类BeliefSet两次:

BeliefSet<float> bSetFloat("SetFloat");
BeliefSet<std::string> bSetString("SetString");

第一次没问题,但在第二次通话时我收到以下错误:

0xC0000005:访问冲突读取位置0x0000000000000000。

有人可以解释为什么会这样,但只有与std::string一起使用?

5 个答案:

答案 0 :(得分:1)

factType是字符串。所以它相当于

string m_Fact = NULL;

答案 1 :(得分:1)

factType InitialFact = NULL其中factType = std::string将尝试使用单个参数std::string构造函数构造const char*。从std::string构建nullptr会导致此次崩溃。

答案 2 :(得分:1)

对于具有通用(取决于模板参数)类型的默认参数,您不需要= 0(这是您使用NULL宏获得的)。它将为std::string而不是默认构造函数选择转换构造函数,并且转换构造函数禁止传递空指针值。崩溃是违反这一先决条件的结果。

您也不希望默认初始化,这会使基元完全没有初始化。 “值初始化”的C ++概念在这里很好用...具有非平凡结构的类型的默认构造,否则为零初始化。

然后好的选项是从值初始化的默认值进行复制初始化,或者使用空列表进行非常方便的列表初始化,这在语法上非常短并且也适用于聚合(因为C ++ 11它提供了值初始化对于标量类型,在此之前它只对聚合有用):

/* C++03 value-initialization */
explicit Belief(std::string UniqueName, factType InitialFact = factType())

/* list-initialization, since C++11 this also works great for scalars */
explicit Belief(std::string UniqueName, factType InitialFact = {})

答案 3 :(得分:1)

正如其他人所说,你的问题是=NULL;下面我详细描述它,并描述如何修复你的代码。

explicit BeliefSet(std::string UniqueName) :
  Belief<factType>(UniqueName)
{}

factType = std::string,来电:

explicit Belief(
  std::string UniqueName,
  std::string InitialFact = NULL
) :
  BeliefRoot(UniqueName),
  m_Fact(InitialFact)
{}

std::string InitialFact = NULL

是非法的。替换为={},为您提供:

class BeliefRoot
{
  std::string m_Name;
  BeliefRoot(std::string Name):
    m_Name(Name)
  {}
};

template <class factType>
class Belief : public BeliefRoot
{
  factType m_Fact;
  explicit Belief(
    std::string UniqueName,
    factType InitialFact = {}
  ):
    BeliefRoot(UniqueName),
    m_Fact(InitialFact)
  {}
};

template <class factType>
class BeliefSet : public Belief<factType>
{
  std::list<factType> m_Facts;
  explicit BeliefSet(std::string UniqueName):
    Belief(UniqueName)
  {}
};

并且您的代码应该可以使用。

答案 4 :(得分:0)

让我们来看看BeliefSet<std::string>在编译时解决的问题:

//We're appending __string to indicate how the type will (roughly) be represented at compiletime.
class Belief__string : public BeliefRoot
{
    std::string m_Fact;
    explicit Belief(std::string UniqueName, std::string InitialFact = 0)
                                            : BeliefRoot(UniqueName), m_Fact(InitialFact)
    { }
};


class BeliefSet__string : public Belief__string
{
    std::list<std::string> m_Facts;
    explicit BeliefSet(std::string UniqueName) : Belief(UniqueName)
    { }
};

因此,立即可疑代码位于中间类:std::string InitialFact = 0。这个结构很差,但几乎可以肯定的是,字符串试图为其分配一个空指针。字符串可以接收const char *类型进行构造,因此它可能尝试从此空指针读取,并且由于解除引用null而立即失败。