我正在寻找更详细的答案,而不是UB是UB。
我有一段遗留代码,我知道这是异常的罪魁祸首。我们以前memset一个向量,一旦它被更改为unordered_map,它就会在销毁时抛出异常。在源代码中,memsetting一个向量与unordered_map有什么不同?它们都与连续的记忆相互作用......
在我们的代码中,我们有这个。 (我为伪代码道歉,但重点应该是可以理解的......)
val broadcast = builder.add(Broadcast[ByteString](2))
source ~> broadcast ~> fileUpload
broadcast ~> mimeTypeDetection
稍后我们会这样做......
class B
{
std::vector<CustomObject> vect;
};
struct STRUCT_A
{
B b;
};
即使我们设置了一个stl :: container,这也完美无缺!但是,如果我们将类B更改为具有映射,则析构函数上会发生异常。
STRUCT_A m_struct_a;
memset(&m_struct_a, 0, sizeof(STRUCT_A));
所以我认为这与连续的矢量有关,所以我把它改成了unordered_map
class B
{
std::map<CustomObject> vect;
};
析构函数仍然抛出异常。我觉得这很有趣,并认为这是一个很好的问题......
答案 0 :(得分:7)
是的,::memset
包含STL容器的类的实例确实是一种绝对疯狂的行为。
更一般地说,您只能在平易可复制的对象上调用memset
。否则,程序的行为是 undefined 。
有关详细信息,请参阅http://en.cppreference.com/w/cpp/string/byte/memset和http://en.cppreference.com/w/cpp/types/is_trivially_copyable
答案 1 :(得分:1)
查看实施容器的来源。
在libstdc ++中,std::vector
的析构函数为CustomObject::~CustomObject()
范围内的每个元素调用[begin(), end())
,然后释放内存。由于您memset
vector
为0,begin() == end() == 0
,因此范围为空,并且永远不会调用成员的析构函数。类似地,解除分配是正常的,因为它在解除分配之前检查存储是否为空。
在std::map
中,析构函数尝试遍历_Rb_tree
的节点,该节点失败,因为根节点具有空指针。在std::unordered_map
中,析构函数调用bucket数组上的__builtin_memset
,该数组失败,因为已清除指向数组的指针。