我有以下三个类:
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 :(得分: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
而立即失败。