簡體   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