调用对象方法时转移自身unique_ptr所有权的C++相关问题
关于C++中调用对象方法时转移
unique_ptr所有权的疑问 先看示例代码:
#include <memory> static unsigned returnValue = 5; void setReturnValue(unsigned u) { returnValue = u; } class MyObject { public: MyObject(unsigned uIN) : u(uIN) {} ~MyObject() { u = 42; } void method(std::unique_ptr<MyObject> uniqPtrToObject) { // Do something fancy with this unique pointer now, // which will consume the object and its data setReturnValue(uniqPtrToObject->getValue()); } unsigned getValue() { return u; } private: unsigned u; // Some relevant object data }; std::unique_ptr<MyObject> GetUniqToObject(unsigned u) { // Get the object in a fancy way. For now, assume it has just been constructed. return std::make_unique<MyObject>(u); } int main() { std::unique_ptr<MyObject> uniqPtrToObject = GetUniqToObject(0); // =================================================== // Oops! uniqPtrToObject->method(std::move(uniqPtrToObject)); // =================================================== return returnValue; }
这段代码中main函数里的uniqPtrToObject->method(std::move(uniqPtrToObject))写法怪异,测试返回预期的0值并非巧合,以下是针对疑问的解答:
疑问解答
1. 这是合法的C++语法吗?
合法。
- C++17及之后:标准明确规定,成员函数调用
E1->E2(args)中,对象表达式E1的求值会先于所有实参的求值。也就是说,会先获取uniqPtrToObject指向的对象地址(确定this指针),再执行std::move(uniqPtrToObject)转移所有权,全程行为明确。 - C++17之前:虽然标准未强制指定对象表达式和实参的求值顺序,但主流编译器的实现都会优先计算对象表达式以确定
this指针,因此不会触发空指针访问这类未定义行为,代码依然合法。
2. 该写法是否可移植?
从实际使用角度看是可移植的。
- C++17及之后:标准明确了求值顺序,所有符合标准的编译器都会产生一致结果。
- C++17之前:主流编译器(GCC、Clang、MSVC)都遵循“先计算对象表达式”的实现逻辑,现实中不会出现行为差异,仅理论上存在标准未覆盖的边缘情况,但几乎不会发生。
3. 该写法是否符合编码规范?
完全不符合。
这种写法可读性极差,极具迷惑性,维护者很难快速理解代码意图,后续修改时极易引入错误。比如若后续调整method逻辑,或在调用前后添加依赖uniqPtrToObject状态的代码,很可能触发空指针访问等问题。
更清晰的写法应该拆分所有权转移的逻辑,比如:
auto temp = std::move(uniqPtrToObject); temp->method(std::move(temp));
或者根据实际需求重新设计接口,比如让method接收对象引用而非unique_ptr(若不需要转移所有权)。
阅读建议
- 学习C标准中表达式求值顺序的章节,重点关注C17带来的规则变化。
- 深入理解
std::unique_ptr的所有权语义,参考官方文档中的使用示例。 - 阅读权威编码规范(如ISOCPP核心指南、Google C++编码规范)中关于智能指针的最佳实践。
内容的提问来源于stack exchange,提问作者HelpingHand
相关产品推荐
相关产品推荐

