C++ shared_ptr调用函数的API设计疑问:*data.get()是否为设计异味?
struct Data { /* ... */ }; int SomeFunction1(const Data& data) { return 0; } int SomeFunction2(std::shared_ptr<Data>& data) { return 0; } // 此处为非const引用 int SomeFunction3(const Data* data) { return 0; } // 可能接收空指针 int main() { auto data = std::make_shared<Data>(); /* 初始化逻辑 */; return SomeFunction1(*data.get()); // (1) 调用get并解引用指针 return SomeFunction2(data); // (2) 设计接收shared_ptr的API return SomeFunction3(data.get()); // (3) 设计接收裸指针的API }
关于*data.get()的写法
*data.get()功能上完全可行,但并不常见,属于冗余写法。因为std::shared_ptr已经重载了解引用运算符operator*(),直接写*data就能达到完全一致的效果,且更简洁、符合C++的惯用编码风格。
方式(1)的合理性分析
你认为方式(1)更可取的判断是正确的,理由如下:
- 通用性最强:无论调用方持有
shared_ptr、unique_ptr还是栈上的Data实例,都能直接传递给接受const Data&的函数,无需任何适配代码。 - 安全性更高:引用不能为空,API无需处理空指针场景,避免了潜在的空指针解引用风险;同时
const修饰明确函数仅读取Data内容,语义清晰。 - 性能最优:传递引用不需要拷贝智能指针,也无额外指针操作开销,效率最高。
*data.get()是否属于设计异味
它算不上严重的设计异味,只是不够地道的编码风格。这种写法暴露了底层指针操作,而智能指针的核心价值之一就是封装指针操作、简化内存管理,直接调用get()再解引用,有点违背智能指针的设计初衷,换成*data更符合封装原则。
另外两种方式的局限性
- 方式(2):接受
std::shared_ptr<Data>&(非const引用)意味着函数可能会修改智能指针本身(比如重置、替换指向对象)。若API不需要此类操作,这种写法会强制调用方必须使用shared_ptr,大幅降低API通用性,只读场景不推荐使用。 - 方式(3):接受
const Data*允许传递空指针,API内部必须额外处理空值逻辑,增加复杂度;若确定不会传空值,使用引用比指针更安全。
总结
方式(1)的API设计是正确方向,但调用时建议将*data.get()改为*data,既简洁又符合C惯用写法。如果函数不需要管理对象生命周期,优先选择传递const T&(只读场景)或T&(可写场景),这是C传递对象的最佳实践之一。
内容的提问来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

