繁体   English   中英

为什么在此C ++代码中出现“访问冲突”

[英]Why do I get an “Access violation” in this C++ code

我有以下三个课程:

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::stringstd::string

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

string m_Fact = NULL;

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

对于具有泛型(取决于模板参数)类型的默认参数,您不需要= 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 = {})

正如其他人指出的那样,您的问题是=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)
  {}
};

并且您的代码应该可以正常工作。

让我们看看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指针读取,并且由于取消引用null而立即失败。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM