今天在写代码的时候突然遇到一个中断问题,但是光看代码根本发现不了错误,代码大概类似于:
typedef struct MemsetTest
{
int year;
map<int,string> m_map;
MemsetTest()
{
memset(this , 0 , sizeof(MemsetTest));
}
}MemsetTest;
int Main()
{
MemsetTest tTest;
tTest.year = 2020;
tTest.m_map[5] = "5";
}
其中为map添加值那一行会报错。
遇到这个问题之后看代码是在想不出问题在哪,然后我就把源代码中MemsetTest的结构体给复制到了一个新的工程中测试,但是还是会爆错,于是我试着删除了MemsetTest的m_map变量,在测试是可以正常运行的;但是变得更迷惑了,map用了没有一千也有八百次了,为什么这里会死机?就继续删代码一点点测试,最后删到发现有memset与map同时存在就会出问题;在网上一搜索了解到问题果然出现在memset上:memset()函数将对象的所有内存都初始化为0,这在初始化的类是一个POD类型(Plain Old Data)时是没有问题的,因为POD类型的二进制内容可以随便复制、移动、重置而不会改变POD类型对象的值。ConfigSettings成员都是POD类型(int,char,float…),而且ConfigSettings类既不存在虚继承也不存在虚函数,所以可以对ConfigSettings使用memset()函数进行初始化。但是因为我在MemsetTest中加入了map类型的成员变量,就导致MemsetTest不再是一个POD类型结构,所以当使用memset()函数进行初始化时会对对象造成破坏,map底层是红黑树,其中必然包括各种指针,当使用memeset将对象所有内存初始化为0的时候,map中的各种指针必然就变成了null_ptr,整个结构已经被完全破坏了,所以会后面对map进行操作的时候就会出现崩溃。
最后解决办法
- 重新写MemsetTest的构造函数,不再使用memset()函数初始化对象。
- 将成员变量map设置为static类型,可以避免在memset()时被破坏.
另外以后一定要注意构造函数初始化尽量不要直接使用memset,因为就算你写的时候是一个POD结构,但随着时间的流逝后续其他人维护就可能添加指针等,到时他如果不了解这个问题就又会困住他好久。