公开接口参数类型应优先选用const t&或t:廉价可复制类型(如int、double)按值传递更高效;大对象(如std::string)必须用const t&避免拷贝;禁用裸指针和非const引用;输入字符串统一用std::string_view;默认参数必须在头文件声明。

公开接口的参数类型选择,本质是平衡调用方便利性、实现方可控性与 ABI 稳定性。选错类型,轻则引发隐式拷贝开销或 const 歧义,重则导致二进制不兼容、跨编译单元行为不一致。
输入参数优先用 const T& 或 T,别无脑传引用
不是所有类型都适合传 const T&:对 int、double、std::size_t 这类廉价可复制类型(通常 ≤ 2–3 个机器字),按值传递更高效,且语义清晰——调用方知道这是“一份快照”,不会被意外修改或生命周期绑架。
对 std::string、std::vector、自定义大结构体等,必须用 const T&。否则每次调用都在栈上复制几百字节,性能断崖式下跌。
- 错误示范:
void process(std::string s)—— 每次调用都触发一次堆分配 + 全量拷贝 - 正确写法:
void process(const std::string& s) - 例外情况:若函数内部确实需要拥有该对象的副本(比如要修改或异步保存),可用
T接收,再 move 构造,但需在文档中明确说明所有权转移语义
避免裸指针和非 const 引用作输入参数
裸指针(T*)作为输入参数,在公开接口中几乎总是坏信号:它模糊了所有权、空值合法性、生命周期归属。调用方无法从签名判断是否可传 nullptr,实现方也得反复加 if (p == nullptr) 防御。
非 const 引用(T&)更危险——它强制要求传左值,且暗示“我会改你”。公开接口中除非明确设计为输出参数(如 bool try_parse(const char* src, int& out_value)),否则一律禁用。
- 替代方案:输入场景下,用
const T&;需要可选/空值语义时,用std::optional<const t></const>(C++17+)或显式std::string_view/std::span等视图类型 - 特别注意
char*:永远不要用它接收字符串输入;统一用std::string_view(C++17)或const std::string&
慎用 std::string 和容器类型作为参数类型
std::string 看似方便,但它是具体实现类型,绑定 STL 版本和内存分配器。若你的库要被不同编译器/标准库版本链接(如 Windows 上 MSVC + MinGW 混用),std::string 可能因 ABI 不兼容直接崩溃。
同理,std::vector、std::map 等标准容器也不适合作为公开 ABI 接口的一部分。
- 安全做法:输入用
std::string_view(只读、零拷贝、无分配器依赖)、std::span<const t></const>(C++20)或 C 风格const T*+size_t成对出现 - 若必须返回容器,返回
std::vector<t></t>是可接受的(调用方拥有所有权),但不要返回std::vector<t>&</t>或const std::vector<t>&</t>—— 生命周期和线程安全全靠约定,极易出错
默认参数值必须在头文件中声明,且不可跨翻译单元不一致
默认参数不是魔法,它在编译期由调用方决定。如果 func(int x, const std::string& s = "default") 的默认值只写在 .cpp 文件里,而头文件没声明,那么所有包含该头文件的调用方都会报 “no matching function” 错误。
- 必须把默认值写在头文件的函数声明中(如
func.h) - 禁止在 .cpp 中重复写默认值,否则编译器报错
default argument given for parameter - 更隐蔽的坑:若头文件被多个间接路径包含(如 A.h → B.h → func.h),而 func.h 本身没加
#pragma once或#ifndef保护,可能因多次展开导致重复声明,默认参数冲突
最易被忽略的一点:公开接口一旦发布,参数类型就锁死了。换 std::string 为 std::string_view 是二进制不兼容变更,哪怕语义完全等价。所以第一次设计就要想清楚——是追求调用简洁,还是 ABI 坚固;很多时候,std::string_view 是那个少踩五年坑的选择。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











