2016-12-09 61 views
0

我有以下三類:爲什麼我會收到「訪問衝突」這個C++代碼

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一起使用?

+0

如果你有C++ 11,你應該開始使用'nullptr'。它會給你一個更好的錯誤,因爲將非指針tpye設置爲NULL是無意義的。 – NathanOliver

+0

在C++中,您不應該再使用宏'NULL'。使用'nullptr',或者爲你正在使用的任何類型使用適當的「零初始化器」。 – Xirema

+2

但是'factType'也可以成爲一個原語,比如'int'。在那種情況下,我不允許使用'nullptr',或者我?我得到以下錯誤,然後:*錯誤C2440:'默認參數':不能從'nullptr'轉換爲'int'* – Matthias

回答

1

對於具有通用(取決於模板參數)類型的默認參數,您不需要= 0(這是您使用宏獲得的結果)。它將爲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 = {}) 
1

factType是字符串。因此,它是相當於

string m_Fact = NULL; 
+0

選擇const char *構造函數....它具有指針不爲null的前提條件。 –

1

factType InitialFact = NULL其中factType = std::string會嘗試使用單個參數const char*構造函數來構造一個std::string。從nullptr構建std::string將導致此崩潰。

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

+0

這裏沒有任務,沒有初始化。除此之外,你是正確的。 –

+0

@BenVoigt我在我的帖子中指出了初始化發生的地方:'std :: string InitialFact = 0'。 – Xirema

+0

@ Ximera:是的,但你稱之爲任務。直接引用「幾乎肯定會發生的事情是字符串試圖將空指針**分配給它」 –

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

和你的代碼應該工作。