C++函数参数,何时该用引用,何时该用指针?
前言在C函数编写过程中你是否曾经考虑过函数参数应该用引用好还是指针好呢这两种参数传递有和区别呢该如何正确选择目录一、区别对比二、何时用引用三、何时用指针一、区别对比首先我们先分析一下引用与指针之间的主要区别。特性引用 (Reference,)指针 (Pointer,*)本质是一个别名 (Alias)。它就是变量本身只是换了个名字。是一个地址 (Address)。它存储了另一个变量的内存地址。可空性不能为空。必须在声明时初始化且不能再指向其他变量。可以为空(nullptr)。表示它不指向任何有效的对象。操作语法可以像普通变量一样直接使用。如ref.member需要解引用 (*或-) 访问其指向的对象。如ptr-member安全性更安全。因为它保证有效不为空所以不需要进行空检查。风险更高。必须在使用前检查是否为nullptr否则可能导致程序崩溃。内存占用不占用内存空间与原变量共享空间占用4字节或8字节空间用途语义需要一个实际的对象并且可能会修改它或者只是想避免拷贝。可能会给一个对象也可能不给不一定。下图我们从内存占用、语法复杂度、安全性及灵活性四个维度进行评估从以上分析不难看出总体来说引用是首选指针是次选。二、何时用引用引用传递因其简洁和安全的特性在C中被广泛作为传递大型对象或需要修改外部变量的首选方式。以下情况应优先考虑使用引用1) 大型对象避免拷贝当你需要传递大型对象包含大量数据的类或结构体时使用引用传递可以避免调用拷贝构造函数从而显著减少性能开销。例如当传递一个std::string或std::vector时使用引用可以避免其内部数据的深拷贝。同时在引用参数前加上const关键字表明函数不会修改该参数这样既能避免拷贝又能保证数据的安全性。例如// 低效方式每次调用都会拷贝整个字符串 void printStringByValue(std::string s) { std::cout s std::endl; } // 高效方式传递引用没有拷贝发生 void printStringByReference(const std::string s) { // s oops; // 编译错误因为 s 是 const std::cout s std::endl; }2) 需要修改传入的参数如果函数的目的是修改调用者传入的变量那么引用是理想的选择。引用参数充当实参的别名函数内部对引用参数的任何修改都会直接作用于原变量非常方便。例如void swap(int a, int b) { int temp a; a b; b temp; } int main() { int x 5, y 10; swap(x, y); // x 现在是 10, y 现在是 5 }这里为什么不用指针这里swap函数必须接收两个有效的int不存在可选的情况发生。三、何时用指针虽然引用在多数情况下更为安全和方便但在某些特定场景下指针仍然是不可或缺的。当需要表达可能为空或需要重新绑定的语义时指针会是更合适的选择。以下情况应考虑使用指针1) 参数是可选的(可能为空)如果函数参数可能没有值参数是可选的或者需要将无值作为一个特殊状态传递那么使用指针比引用更直观。指针可以为空nullptr这明确表示该参数未被提供。例如// 假设一个函数它会查找用户如果找到了就通过输出参数返回用户信息 // 如果没找到就不会修改输出参数。 // 使用指针作为可选的输出参数 bool findUser(int userId, User* outUser) { // outUser 是可选的 // ... 查找逻辑 ... if (user_found) { if (outUser ! nullptr) { // 必须检查指针是否为空 *outUser found_user_data; } return true; } return false; } int main() { User u; // 我需要用户信息 if (findUser(101, u)) { // 这里使用 u } // 我只关心用户是否存在不需要具体信息 if (findUser(102, nullptr)) { std::cout User 102 exists. std::endl; } }【注现代 C 替代方案对于可选参数C17 引入了std::optional它在类型层面就表达了“可选”的语义比指针更安全。】2) 需要重新绑定目标如果函数内部需要改变指针本身的指向例如 让指针指向另一个对象或者让指针变为空那么必须使用指针作为参数。引用一旦绑定就无法更改指向例如void allocateInt(int** ptr) { *ptr new int(42); // 让传入的指针指向新分配的内存 }3) 需要与C库进行交互C 语言没有引用所有通过地址传递的参数都使用指针。当你需要调用一个 C 函数或者一个遵循 C 风格的 C 库时你必须使用指针。例如// C 语言库中的一个函数 void process_data(const double* data, int size); // 在 C 中调用它 std::vectordouble my_data {1.0, 2.0, 3.0}; // vector::data() 返回一个指向其内部数组的指针 // process_data(my_data.data(), my_data.size());