现代C++中push接口提供const T&与T&&两个重载,是为了分别支持左值的只读借用和右值的资源接管,兼顾性能、类型安全与移动语义需求。

接口函数参数用 const T& 还是 T&&?看资源是否移交
接口里传参不是语法练习,而是明确表达所有权意图。用 const T& 表示“只读借用”,调用方仍完全拥有该对象;用 T&&(右值引用)表示“你接管这个将亡对象的资源”,调用方已放弃控制权。
常见错误现象:把一个临时对象传给 const T& 参数,以为能触发移动——不会。它只是绑定到 const 左值引用,资源仍在原处,没移交。
-
void process(const Buffer& b):安全读取,b的内存、句柄等全归调用方管,接口不碰析构也不偷数据 -
void accept(Buffer&& b):接口内部大概率会执行std::move(b)把_data指针拿走,b之后处于有效但未定义状态(如_data == nullptr) - 如果接口既想支持借用又想支持接管,得重载:两个版本都提供,否则调用方只能靠
std::move(x)显式“投降”资源
返回值用 T 还是 std::unique_ptr<t></t>?看谁负责销毁
返回裸对象 T 是最直接的“移交所有权”方式——调用方拿到的是副本或移动后的对象,生命周期由其作用域决定;返回 std::unique_ptr<t></t> 则是显式声明“这资源归你管,别忘了 delete(或 let it go)”。
容易踩的坑:返回 T* 或 std::shared_ptr<t></t> 而不说明所有权归属。前者极易导致悬空指针,后者可能引发意外的共享生命周期,违背接口轻量解耦的本意。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 返回
Buffer create_buffer(size_t n):推荐。利用 RVO 或移动语义,零拷贝交付,调用方栈上持有,析构自动清理 - 返回
std::unique_ptr<databaseconnection></databaseconnection>:合理。连接资源昂贵,不应被复制,且需延迟初始化或工厂构造 - 避免返回
DatabaseConnection*:调用方无法判断是否要delete,也无法区分是 new 出来的还是静态对象地址
接口中禁止出现裸指针成员,否则借用/接管边界彻底模糊
一旦在抽象类里加了 char* data_ 或 FILE* file_,这个接口就不再是契约,而成了资源管理陷阱。派生类实现时,没人知道该不该 delete[] data_、该不该 fclose(file_) ——借用和接管混为一谈,多态立刻失效。
正确做法是把资源封装进 RAII 类型:用 std::vector<char></char> 替代 char*,用 std::ifstream 或自定义 FileHandle 替代 FILE*。这样接口只声明行为(read(), write()),资源归属由封装类型自身保证。
- 错误示例:
class IStream { protected: FILE* fp_; };——fp_归谁关?子类必须猜 - 正确示例:
class IStream { public: virtual size_t read(void*, size_t) = 0; };—— 具体怎么读、资源在哪,由实现类自己 RAII 封装 - 若真需暴露底层句柄(如调试或对接 C API),应提供
native_handle()这类明确命名的只读访问函数,而非公开成员
虚析构函数不是可选项,是借用/接管逻辑的守门人
没有虚析构函数的接口类,只要用基类指针接收派生类对象,就一定会跳过派生类析构逻辑。此时哪怕你用了 std::unique_ptr<ifoo></ifoo>,资源也只释放了一半——派生类里 RAII 封装的资源(比如文件、锁、内存)全漏掉了。
这不是风格问题,是未定义行为。而且它往往不 crash,只悄悄泄漏,直到压测时内存曲线突然翘尾。
- 所有含纯虚函数的接口类,
virtual ~InterfaceName() = default;必须写在第一行 public 区域 - 不要写
~InterfaceName() {}—— 空实现不等于虚函数,编译器不会生成 vtable 条目 - 如果接口类带模板参数(如
template<typename t> class IContainer</typename>),虚析构也得是模板化的,不能只在特化里补
接口本身不管理资源,它只靠参数、返回值、成员设计和析构约定来划清借用与接管的线。这条线画歪了,再多的 std::move 和 unique_ptr 都救不回混乱的所有权语义。

















