std::map插入对象时构造函数内变量地址异常问题咨询
拆解你的C++代码问题:拷贝构造引发的悬空指针陷阱
嘿,咱们一步步来理清楚你遇到的问题:
为什么第一个代码里地址不一样,还出现垃圾值?
当你执行map.insert(std::make_pair(4, exampleClass{}));的时候,背后发生了这些事:
- 你先创建了一个临时的
exampleClass对象(就是exampleClass{}),这个对象住在栈内存里,也就是构造函数打印的0x7ffee080b500地址。这个临时对象的innerMap里存了指向自己成员a的指针。 - 接着
std::make_pair会把这个临时对象完整拷贝一份,做成pair的第二个元素;然后map.insert又把这个pair拷贝到map自己管理的内存里(通常是堆内存,也就是你循环里看到的0x559ddb2c42e8地址)。 - 临时对象完成拷贝任务后,会立刻被销毁——但你之前存在
innerMap里的指针,还死死指着那个已经被销毁的临时对象的a的地址!这就成了悬空指针,当你最后一行解引用这个指针时,那块内存已经被系统回收或者被其他数据覆盖了,自然会输出莫名其妙的垃圾值(比如6.95312e-310)。
至于构造函数和循环里的地址不一样?那是因为循环里的对象是临时对象的拷贝副本,它和原临时对象是完全独立的两个实例,一个在堆里,一个在栈里,地址当然不同啦。
为什么第二个代码能正常运行?
你直接写exampleClass abc{};的时候,这个abc是一个在栈上持久存在的对象,它的生命周期覆盖了后面所有的打印操作——从创建到你打印完,abc一直活着,所以innerMap里的指针始终指向有效的内存(abc.a的地址),解引用自然能得到正确的12.1。
怎么解决第一个代码的问题?
给你几个实用的方案:
- 方案一:直接在map里构造对象,避免拷贝
用emplace配合piecewise_construct,让exampleClass直接在map的内存里构造,完全跳过临时对象的环节:
这样构造出来的对象,map.emplace(std::piecewise_construct, std::forward_as_tuple(4), std::forward_as_tuple());innerMap里的指针指向的就是自己的a,不会有悬空问题。 - 方案二:不要存指针,直接存值
如果业务逻辑允许,把innerMap的类型改成std::map<int, double>,直接存a的值而不是指针,从根源上避免指针悬空的风险。 - 方案三:自定义拷贝构造函数,修正指针指向
当对象被拷贝时,手动更新innerMap里的指针,让它指向新对象的a:
这样拷贝出来的对象,struct exampleClass { double a = 12.1; std::map<int, double*> innerMap; exampleClass() { std::cout << "ADR of variable a in constructor = " << &a << std::endl; std::cout << "ADR of this in constructor = " << this << std::endl; innerMap.emplace(4,&a); } // 自定义拷贝构造函数 exampleClass(const exampleClass& other) : a(other.a), innerMap(other.innerMap) { // 将innerMap中的指针替换为当前对象的a的地址 innerMap[4] = &a; } };innerMap里的指针就指向自己的a了,不会再悬空。
内容的提问来源于stack exchange,提问作者Peter Lustig
相关产品推荐
相关产品推荐

