C++ 参数传递
一、核心参数传递机制
1. 值传递 (Pass by Value)
- 机制:创建实参的完整副本
- 特点:
- 函数内修改不影响原始数据
- 触发拷贝构造和析构
- 适合基本类型和小型对象
- 示例:
void modify(int val) {
val = 100; // 仅修改副本
cout << "Inside: " << val << endl;
}
int main() {
int x = 10;
modify(x);
cout << "Outside: " << x; // 输出 10
}
2. 引用传递 (Pass by Reference)
- 机制:传递实参的别名
- 类型:
- 左值引用 (
T&
): 可修改原始数据 - 常量引用 (
const T&
): 只读访问,避免拷贝
- 左值引用 (
- 示例:
void increment(int& ref) {
ref++; // 直接修改原始数据
}
void print(const vector<int>& vec) {
// 只读访问,避免大型vector拷贝
for (const auto& item : vec)
cout << item << " ";
}
int main() {
int a = 5;
increment(a); // a 变为 6
vector<int> bigData(1000000, 42);
print(bigData); // 高效传递
}
3. 指针传递 (Pass by Pointer)
- 机制:传递内存地址
- 特点:
- 需显式取址(
&
)和解引用(*
) - 支持
nullptr
表示可选参数 - 兼容C语言接口
- 需显式取址(
- 示例:
void init(int* ptr, int size, int value) {
if (ptr) { // 检查空指针
for (int i = 0; i < size; i++)
ptr[i] = value;
}
}
int main() {
int* arr = new int[10];
init(arr, 10, 0); // 初始化数组
init(nullptr, 0, 0); // 安全处理空指针
delete[] arr;
}
二、现代C++增强机制
1. 右值引用与移动语义
- 目的:优化资源管理,避免不必要的拷贝
- 关键语法:
T&&
+std::move()
- 示例:
class Buffer {
int* data;
size_t size;
public:
// 移动构造函数
Buffer(Buffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr; // 转移所有权
}
};
void processBuffer(Buffer&& buf) {
// 接管临时对象的资源
}
int main() {
Buffer temp = createBuffer();
processBuffer(std::move(temp)); // 显式转移所有权
}
2. 完美转发 (Perfect Forwarding)
- 目的:保持参数原始值类别(左值/右值)
- 关键语法:
T&&
+std::forward<T>()
- 示例:
template <typename T>
void relay(T&& arg) {
// 保持arg的左右值属性
process(std::forward<T>(arg));
}
void process(int& x) { cout << "Lvalue\n"; }
void process(int&& x) { cout << "Rvalue\n"; }
int main() {
int a = 10;
relay(a); // 调用左值版本
relay(20); // 调用右值版本
relay(std::move(a)); // 调用右值版本
}
三、特殊类型参数处理
1. 数组参数传递
方法 | 示例 | 特点 |
---|---|---|
指针+大小 | void f(int* arr, size_t n) | 传统方式,需传递大小 |
数组引用 | void f(int (&arr)[N]) | 保留大小信息 |
std::array | void f(const array<int, N>& arr) | 推荐方式,安全高效 |
// 使用std::array (C++11)
void processArray(const array<int, 5>& arr) {
for (auto item : arr)
cout << item << " ";
}
int main() {
array<int, 5> nums = {1,2,3,4,5};
processArray(nums);
}
2. 函数指针与可调用对象
// 函数指针作为参数
void transform(vector<int>& data, int (*func)(int)) {
for (auto& item : data)
item = func(item);
}
// 使用std::function (更灵活)
void process(vector<int>& data, function<int(int)> op) {
for (auto& item : data)
item = op(item);
}
int main() {
vector<int> values = {1,2,3};
// 传递函数指针
transform(values, [](int x) { return x*x; });
// 传递lambda表达式
process(values, [](int x) { return x*2; });
}
3. 可选参数实现
// 传统指针方式
void connect(string host, int port, string* username = nullptr) {
// ...
if (username) {
authenticate(*username);
}
}
// 现代方式 (C++17)
void connect(string host, int port, optional<string> username = nullopt) {
// ...
if (username) {
authenticate(*username);
}
}
int main() {
// 传统方式
string user = "admin";
connect("localhost", 8080, &user);
// 现代方式
connect("db.server", 3306, "root");
connect("cache.server", 11211); // 无用户名
}
四、性能分析与最佳实践
1. 性能比较指南
数据类型 | 推荐方式 | 原因 |
---|---|---|
基本类型 (int/double) | 值传递 | 拷贝开销小于间接访问 |
小型结构体 (<16字节) | 值传递 | 可能比引用更高效 |
大型对象 (vector等) | const T& (只读) / T& (修改) | 避免拷贝开销 |
资源管理类 | T&& (移动语义) | 转移资源所有权 |
可选参数 | T* 或 std::optional | 明确表示参数缺失 |
多态对象 | 基类引用/指针 | 支持运行时多态 |
2. 最佳实践总结
-
默认选择:
- 输入参数:
const T&
(大型对象) 或值传递 (小型数据) - 输出参数:
T&
(修改原对象) - 输入/输出参数:
T&
(同时需要读写)
- 输入参数:
-
移动语义优化:
// 优先使用移动构造 vector<string> processData(vector<string>&& input) { vector<string> result; for (auto& str : input) { if (isValid(str)) result.push_back(std::move(str)); // 移动而非拷贝 } return result; // 返回值优化(RVO) }
-
const正确性:
void calculate(const Matrix& m) { // 保证不修改输入 // 只读操作 }
-
完美转发模板:
template <typename... Args> auto create(Args&&... args) { return T(std::forward<Args>(args)...); }
-
安全边界检查:
void safeAccess(vector<int>& vec, size_t index) { if (index >= vec.size()) { throw out_of_range("Index invalid"); } // 安全访问... }
-
智能指针管理:
void processResource(unique_ptr<Resource> res) { // 明确所有权转移 }
五、高级主题
1. 参数依赖查找 (ADL)
namespace Graphics {
struct Point { int x, y; };
void draw(Point p) { /* ... */ }
}
int main() {
Graphics::Point p{10,20};
draw(p); // ADL找到Graphics::draw
}
2. 变参模板 (Variadic Templates)
template <typename... Args>
void log(Args&&... args) {
(cout << ... << args) << endl; // C++17折叠表达式
}
int main() {
log("Error: ", 404, " Not Found"); // 自动处理不同类型
}
3. 类型萃取 (Type Traits)
template <typename T>
void process(T&& value) {
if constexpr (is_integral_v<T>) {
// 整数类型专用处理
} else if constexpr (is_floating_point_v<T>) {
// 浮点类型专用处理
}
}
总结
C++参数传递是高效编程的核心技术,选择策略应考虑:
- 数据特性:大小、生命周期、所有权
- 操作需求:只读、修改、资源转移
- 性能影响:拷贝开销 vs 间接访问
- 安全需求:空值处理、边界检查、异常安全
现代C++最佳实践:
- 大型只读对象:
const T&
- 需要修改的对象:
T&
- 资源管理类:移动语义(
T&&
) - 可选参数:
std::optional
- 泛型编程:完美转发(
T&&
+std::forward
) - 多态对象:基类指针/引用