简体   繁体   English

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

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

I have the following three classes: 我有以下三个课程:

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)
    { }
};

Now I want to instantiate the class BeliefSet two times: 现在我想两次实例化BeliefSet类:

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

The first is fine, but at the second call I get the following error: 第一个很好,但是在第二个调用中,出现以下错误:

0xC0000005: Access violation reading location 0x0000000000000000. 0xC0000005:访问冲突读取位置0x0000000000000000。

Can somebody explain why this happens, but only if used with std::string ? 有人可以解释为什么会发生这种情况吗,但std::stringstd::string

factType is string. factType是字符串。 So it is equivalent to 所以这相当于

string m_Fact = NULL;

factType InitialFact = NULL where factType = std::string will attempt to construct a std::string using the single argument const char* constructor. factType InitialFact = NULL ,其中factType = std::string将尝试使用单个参数const char*构造const char*构造std::string Constructing a std::string from a nullptr will cause this crash. nullptr构造std::string会导致此崩溃。

For a default parameter having generic (dependent on template parameters) type, you don't want = 0 (which is what you get with the NULL macro). 对于具有泛型(取决于模板参数)类型的默认参数,您不需要= 0 (这是使用NULL宏得到的)。 It'll select a converting constructor for std::string instead of the default constructor, and that converting constructor forbids passing a null pointer value. 它将为std::string选择一个转换构造函数,而不是默认构造函数,并且该转换构造函数禁止传递空指针值。 The crash is a consequence of violating that precondition. 崩溃是违反该先决条件的结果。

You also don't want default initialization, which leaves primitives with no initialization at all. 您也不需要默认初始化,这将使基元完全不进行初始化。 The C++ concept of "value-initialization" serves you well here... default construction for types with non-trivial construction, and zero initialization otherwise. C ++的“值初始化”概念在这里可以为您提供良好的服务...具有非平凡构造的类型的默认构造,否则为零初始化。

Good options then are copy-initialization from a value-initialized default, or the very handy list-initialization with an empty list, which is very short syntactically and also works for aggregates (since C++11 it gives value-initialization for scalar types, before that it was only useful for aggregates): 好的选择是从值初始化的默认值进行复制初始化,或者使用空列表进行非常方便的列表初始化,这在语法上非常短,并且也适用于聚合(因为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 = {})

As others have noted, your problem is the =NULL ; 正如其他人指出的那样,您的问题是=NULL below I describe it in detail, and also describe how to fix your code. 下面我将详细描述它,并描述如何修复您的代码。

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

with factType = std::string , calls: 使用factType = std::string ,调用:

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

and

std::string InitialFact = NULL

is illegal. 是非法的。 Replace with ={} , giving you: 替换为={} ,为您提供:

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)
  {}
};

and your code should work. 并且您的代码应该可以正常工作。

Let's take a look at what BeliefSet<std::string> resolves to at compiletime: 让我们看看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)
    { }
};

So the immediately suspicious code is in the intermediate class: std::string InitialFact = 0 . 因此,立即可疑的代码位于中间类中: std::string InitialFact = 0 This is poorly formed, but what's almost certainly happening is the string is trying to have a null pointer assigned to it. 它的格式不正确,但是几乎可以肯定发生的是字符串正在尝试为其分配空指针。 Strings can receive const char * types for construction, so it may be trying to read from this null pointer, and immediately failing due to dereferencing null . 字符串可以接收const char *类型进行构造,因此它可能试图从此null指针读取,并且由于取消引用null而立即失败。

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

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