C风格指针值异常但智能指针值正确的C++代码问题分析
问题分析:Foo类指针失效而Bar类正常的原因
咱们直接戳破核心——Foo里的指针指向了已经被销毁的临时对象内存,属于典型的悬空指针问题;而Bar是把值复制到了自己管理的独立内存中,所以能正常工作。
一、Foo类的坑:悬空指针是怎么来的?
先看Foo的构造函数代码:
Foo(std::vector<int> vec) { a = &(vec.front()); }
这里的vec是按值传递的参数——也就是说,当你调用Foo foo(vec);时,程序会创建一个原vec的拷贝,这个拷贝只在Foo的构造函数执行期间存在。
一旦构造函数执行完毕,这个临时的拷贝vector就会被销毁,它所管理的内存(包括里面存储的int元素)也会被系统回收。而你让foo.a指向的,正是这个临时vector里第一个元素的地址。现在这个地址对应的内存已经不属于你的程序了,这就是悬空指针。
当你后续解引用*foo.a时,行为是完全未定义的——输出0只是碰巧的结果,它也可能输出随机垃圾值,甚至直接导致程序崩溃。
二、Bar类为什么能正常输出42?
再看Bar的构造函数:
Bar(std::vector<int> vec) { a = std::make_unique<int>(vec.front()); }
std::make_unique<int>(vec.front())做了两件关键的事:
- 先读取临时拷贝vector里第一个元素的值(也就是42)
- 在堆上分配一块全新的内存,把42复制进去,然后让
unique_ptr接管这块内存的生命周期。
哪怕构造函数结束后临时vector被销毁,也完全不影响——因为Bar的a指向的是独立的堆内存,里面存的是42的副本,和原来的vector没有任何关联。所以后续解引用*bar.a时,自然能拿到正确的42。
三、如何修复Foo类?
如果想让Foo也能正确工作,有几个可行的思路:
- 把构造函数参数改成引用传递,避免创建临时拷贝:
Foo(std::vector<int>& vec),这样vec就是原vector的引用,只要原vector的生命周期比Foo对象长,指针就会一直有效。 - 放弃指针,直接存储值:把
int* a;改成int a;,构造函数里直接赋值a = vec.front();,彻底避开指针问题。 - 如果必须用指针,也可以改用智能指针管理,但这个场景下其实没必要,前两种方案更简单直接。
内容的提问来源于stack exchange,提问作者justicecurcian
相关产品推荐
相关产品推荐

