一、本质区别
1、值传递是赋值粘贴,函数内获取的副本,不影响外部
2、指针传递的是"邮寄地址",可通过地址间接修改外部对象
3、引用传递的是别名绑定,可直接操作原对象
二、比对
|------------|-------------|---------------|---------------|
| 对比维度 | 值传递 | 指针传递 | 引用传递 |
| 传递内容 | 实参的副本(全新拷贝) | 实参地址(拷贝地址值) | 实参的别名(底层也是地址) |
| 能否修改外部实参 | 不能(改的是副本) | 能(通过解引用*ptr) | 能(直接操作原对象) |
| 内存开销 | 打(拷贝整个对象) | 小(拷贝8字节地址) | 小 |
| 空值(null)风险 | 无 | 有(空指针需判断空) | 无(引用必须事先绑定对象) |
- 代码:
cpp
#include <iostream>
#include <string>
// 1. 值传递:改的是副本
void byValue(std::string s) {
s += "_copy"; // 只影响局部副本
}
// 2. 指针传递:通过地址修改原对象
void byPointer(std::string* s) {
if (s) { // 必须判空!
*s += "_ptr";
}
}
// 3. 引用传递:直接修改原对象
void byReference(std::string& s) {
s += "_ref";
}
int main() {
std::string str = "Hello";
byValue(str);
std::cout << str << std::endl; // 输出 "Hello"(未变)
byPointer(&str);
std::cout << str << std::endl; // 输出 "Hello_ptr"
byReference(str);
std::cout << str << std::endl; // 输出 "Hello_ptr_ref"
}
三、引用使用场景
1、需要修改实参
cpp
void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; }
2、传递大对象,只读不写使用const引用
cpp
void printVector(const std::vector<int>& v) { // 避免拷贝百万个元素
for (int x : v) std::cout << x;
}
3、保留派生类信息,实现多态
cpp
class Animal { virtual void speak(){} };
class Cat : public Animal { void speak() override {} };
void play(Animal& a) { a.speak(); } // 传 Cat 也能正确调用 Cat::speak()
4、运算符重载
cpp
int& operator[](size_t idx) { return data[idx]; }
四、使用指针的场景
1、参数可选,可能为空
2、需要动态改变指针指向的对象
- 指针可指向另外的对象地址,而引用不允许