You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

用new与已有对象地址初始化指针的差异及生命周期、泄漏问题

new初始化指针 vs 栈对象地址初始化指针:差异全解析

这两种初始化指针的方式差异巨大,核心根源在于对象分配的内存区域不同——一个在堆,一个在栈,直接导致了生命周期、风险的天差地别,咱们结合你的代码一步步拆解:

1. 对象生命周期:完全不同的“存活规则”

new Apple()创建的堆对象

这个对象被分配在堆内存里,它的生命周期完全由你掌控:只有当你显式调用delete ptr时,它才会被销毁,内存被回收。如果一直不调用delete,它会一直占用内存,直到整个程序退出。

对应你代码里的doSomething():返回的ptr1指向的堆对象,在main()函数结束后依然“活着”——只是指针变量ptr1本身(存在栈上)被销毁了,堆里的Apple对象还在原地,没人管它。

用栈对象地址初始化的指针

局部对象Apple a是在doSomethingElse()栈帧里创建的,栈帧的生命周期和函数绑定:当函数执行完毕(也就是doSomethingElse()返回时),这个栈帧会被销毁,里面的所有局部变量(包括a)都会被自动销毁,内存被系统回收。

对应你代码里的ptr2:它拿到的是一个即将失效的地址——doSomethingElse()返回后,a已经不存在了,ptr2变成了野指针(也叫悬垂指针)。后续如果尝试通过ptr2访问对象的成员,会触发未定义行为:可能程序直接崩溃,可能输出乱码,甚至可能看起来“正常”但埋下隐藏bug,排查起来特别头疼。

2. 内存风险:泄漏 vs 野指针,哪个更危险?

new创建的对象:明确的内存泄漏风险

如果你忘记调用delete,堆对象就会一直占用内存,这就是内存泄漏。比如你的main()函数里,拿到ptr1后没有执行delete ptr1;,这个Apple对象的内存就永远不会被释放——如果是短期运行的小程序,可能看不出问题,但如果是长期运行的服务端程序,这种泄漏会不断消耗内存,最终导致程序崩溃。

栈对象的指针:无泄漏,但野指针更致命

栈对象会被自动销毁,所以不存在内存泄漏的问题,但返回栈对象的地址是绝对的错误操作:野指针的访问行为完全不可预测,比内存泄漏更危险——泄漏只是浪费内存,而野指针可能直接破坏程序的内存结构,导致程序崩溃或数据损坏。

3. 针对你的代码逐段拆解

doSomething()的完整流程

  1. Apple* ptr = new Apple();:在堆上开辟一块内存,创建Apple对象,ptr指向这块内存。
  2. 执行业务逻辑后返回ptrmain()里的ptr1拿到这个堆对象的地址。
  3. main()结束时,ptr1(指针变量本身,存在栈上)被销毁,但堆里的Apple对象依然存活——这就造成了内存泄漏,解决方法是在main()末尾加上delete ptr1;

doSomethingElse()的完整流程

  1. Apple a {/* 参数 */};:在doSomethingElse()的栈帧上创建Apple对象。
  2. Apple* ptr = &a;:指针指向这个栈对象。
  3. 返回ptrmain()里的ptr2拿到的是一个已经失效的地址——因为doSomethingElse()返回后,栈帧被销毁,a已经不存在了。
  4. ptr2此时是野指针,任何对它的解引用操作(比如ptr2->someMethod())都是未定义行为,绝对不能这么做!

总结

  • 堆对象(new创建):适合需要长期存活、跨函数传递的对象,但必须手动管理生命周期,记得用delete释放,否则会泄漏。更推荐用智能指针(比如std::unique_ptr)来自动管理,避免手动delete的失误。
  • 栈对象的指针:绝对不要返回栈对象的地址!如果需要从函数返回对象,要么直接返回对象值(利用C++的返回值优化,不会有性能问题),要么返回堆对象的指针/智能指针。

内容的提问来源于stack exchange,提问作者Remellion

火山引擎 最新活动