我无法理解为什么这不符合我的预期。可能是我正在使用Visual Studio 2013,但是嘿。
此代码是我正在撰写的游戏引擎中项目随机化系统的一部分。
// the chance a rarity will have a given number of affixes
std::unordered_map<ItemRarities, ChanceSelector<int>> affixCountChances = {
std::pair<ItemRarities, ChanceSelector<int>>(ItemRarities::Cracked,
{ ChanceSelector<int>(
{ ChancePair(int, 100, 0) }) }),
std::pair<ItemRarities, ChanceSelector<int>>(ItemRarities::Normal,
{ ChanceSelector<int>(
{ ChancePair(int, 80, 0),
ChancePair(int, 20, 1) }) }),
// snip for conciseness (there are 3 more)
};
这是ChanceSelector类:
using Percentage = int;
#define ChancePair(T, p, v) std::pair<Percentage, T>(p, v)
template <class T>
class ChanceSelector
{
private:
std::unordered_map<T, Percentage> _stuff;
public:
ChanceSelector()
{
}
~ChanceSelector()
{
if (_stuff.size() > 0)
_stuff.clear();
}
ChanceSelector(std::initializer_list<std::pair<Percentage, T>> list)
{
// snip for conciseness
}
T Choose()
{
// snip for conciseness
}
};
上面的代码编译得很好,但我有两个问题:
Unhandled exception at 0x01762fec in (my
executable): 0xC0000005: Access violation reading location 0xfeeefeee.
如果我在该地图中只有一个项目,或者我将affixCountChances的定义更改为std::unordered_map<ItemRarities, ChanceSelector<int>*>
(并相应地调整其余部分),则数字2消失。该错误会将此代码转储到list
:
for (_Nodeptr _Pnext; _Pnode != this->_Myhead; _Pnode = _Pnext)
{
_Pnext = this->_Nextnode(_Pnode); // <-- this line
this->_Freenode(_Pnode);
}
进一步检查揭示了析构函数中发生的错误。 _stuff
为空:
~ChanceSelector()
{
if (_stuff.size() > 0)
_stuff.clear();
}
它合法地称为析构函数。项目正在从_stuff
中删除,但我不明白为什么它会调用析构函数。在构造完所有项目并且affixCountChances包含所有项目之后发生崩溃。我认为这意味着它正在摧毁它所创造的所有临时工,但我不明白为什么它会创造临时工。
编辑:
ChanceSelector的构造函数:
ChanceSelector(std::initializer_list<std::pair<Percentage, T>> list)
{
int total = 0;
int last = 100;
for (auto& item : list)
{
last = item.first;
total += item.first;
_stuff[item.second] = total;
}
// total must equal 100 so that Choose always picks something
assert(total == 100);
}
答案 0 :(得分:1)
回答你的两个问题:
std::pair
需要默认构造函数,因为您可以执行类似
std::pair<int, MyClass> myPair();
使用默认构造函数创建类的副本(对的值是实际值而不是引用):
// MSVC implementation
template<class _Ty1,class _Ty2>
struct pair
{ // store a pair of values
typedef pair<_Ty1, _Ty2> _Myt;
typedef _Ty1 first_type;
typedef _Ty2 second_type;
pair()
: first(), second() // Here your class gets default constructed
{ // default construct
}
// .....
_Ty1 first; // the first stored value
_Ty2 second; // the second stored value
};
该对的模板完全实现,因此如果您不使用上面的行,则需要一个默认的构造函数。
避免这种依赖性的一种方法是使用std::pair
中的指针,然后将该对的第二个值的默认值设置为nullptr
:
std::pair<int, MyClass*> myPair();
0xFEEEFEEE
表示存储指针本身的存储已被删除(例如,处理已删除的类引用)。
此删除似乎发生在您在此处发布的代码之外的某处。
有关Magic Numbers
的更多信息,请参阅Magic Numbers on Wikipedia
编辑:
此外,构造函数调用后,初始化程序列表的内容不存在。您可能会复制引用而不是实际对象,然后将其删除。 std::unordered_map
的msvc实现使用std::list
作为存储项目的基础。我无法通过给定的代码提供有关此内容的更多信息。
Initializer list and lifetime of its content
编辑2:我能够使用您给定的代码重现错误,它不是initializer_list ctor的内容。 问题似乎是初始化列表中对象的生命周期。
当我将无序映射的对的声明移出无序映射的initializer_list时,一切正常:
std::pair<ItemRarities, ChanceSelector<int>> pair1( ItemRarities::Cracked,
{ ChanceSelector<int>(
{ ChancePair( int, 100, 0 ) } ) } );
std::pair<ItemRarities, ChanceSelector<int>> pair2( ItemRarities::Normal,
{ ChanceSelector<int>(
{ ChancePair( int, 80, 0 ),
ChancePair( int, 20, 1 ) } ) } );
std::unordered_map<ItemRarities, ChanceSelector<int>> chances = {
pair1,
pair2
};
我不完全确定这是一个问题,但我认为它来自初始化列表中的{}
,当离开第一个{}
时,这些对象可能会被删除在输入unordered_map的实际intializer_list之前