取C++临时对象的地址是否安全可靠?
取临时对象地址是否安全?
先给个直接结论:你当前的代码碰巧能正常运行,但这种写法本身不符合C++标准,依赖编译器的宽容设置,而且稍有不慎就会触发严重的未定义行为,绝对算不上“安全可靠”。
为什么当前代码能运行?
C++里临时对象(比如Number(456)这种无名临时实例)的生命周期规则是:它会在所在的完整表达式结束时被销毁。这里的完整表达式就是print(&Number(456));这整一行——也就是说,只有当print函数执行完毕、整个语句结束后,这个临时对象才会被析构。所以在print函数内部,指针指向的对象是完全有效的,这也是你能得到正确输出的原因。
为什么需要-fpermissive?
因为C++标准明确禁止取临时对象的地址——这属于语法错误。GCC的-fpermissive选项会把这类标准错误降级为警告,允许代码编译通过,但这种写法是不具备移植性的:换用更严格的编译器(比如Clang默认配置),或者开启严格编译模式(-pedantic-errors),代码直接编译失败。
哪些情况会出问题?
一旦你把临时对象的指针带出其生命周期范围,立刻就会出现悬空指针(指向已经被销毁的对象),触发未定义行为(崩溃、乱码、程序逻辑异常都是可能的),典型场景包括:
- 存储指针供后续使用:如果
print函数不是直接使用指针,而是把它存在全局变量、类成员或者其他能跨表达式访问的地方,之后再去访问这个指针,临时对象已经不存在了:Number* global_num_ptr; void save_ptr(Number* num) { global_num_ptr = num; } int main() { save_ptr(&Number(456)); std::cout << global_num_ptr->value; // 未定义行为:临时对象已销毁 } - 跨线程使用指针:如果函数内部启动了一个线程,线程延迟访问指针,但主线程的完整表达式已经结束,临时对象已被销毁:
#include <thread> void delayed_print(Number* num) { std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << num->value << std::endl; // 悬空指针! } int main() { std::thread t(delayed_print, &Number(456)); t.detach(); } - 返回临时对象的指针:如果函数返回临时对象的地址,调用方拿到的直接就是悬空指针:
Number* create_temp_num() { return &Number(789); // 返回临时对象地址,函数结束后对象销毁 }
正确的替代方案
要避免这类问题,最稳妥的方式是:
- 像你创建
example那样,使用命名对象,再取地址传递; - 把函数参数改成
const T&(常量引用),这样可以直接传递临时对象,标准会自动延长临时对象的生命周期到函数调用结束,完全合法且安全:void print(const Number& number) { std::cout << number.value << std::endl; } // 调用时直接传临时对象,安全合规 print(Number(456));
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

