这个条款揭示了C++函数参数传递的核心优化策略:通过const引用传递可以避免不必要的对象拷贝,同时保持语义的正确性。这是构建高性能C++系统的关键技巧,需要在效率、安全性和表达力之间找到平衡。
思维导图:参数传递策略的完整体系

关键洞见与行动指南
必须遵守的核心原则:
- 默认使用const引用:对于非内置类型,优先使用const引用传递
- 避免对象切片:多态基类必须使用引用或指针传递
- 考虑移动语义:对于可移动类型,评估传值与移动的权衡
- 测量性能影响:基于实际性能数据做出传递策略决策
现代C++开发建议:
- 移动语义利用:对于资源管理类,考虑传值+移动的优化模式
- 完美转发应用:模板函数中使用通用引用保持值类别
- 概念约束使用:通过概念在编译期选择最优传递策略
- 拷贝省略依赖:利用RVO/NRVO优化返回值传递
设计原则总结:
- 零开销抽象:在不牺牲性能的前提下提供高级抽象
- 语义正确性:传递方式不应改变程序的逻辑行为
- 接口清晰性:参数传递意图应该在接口中明确表达
- 实践导向:基于实际性能特征而非理论假设做决策
需要警惕的陷阱:
- 悬空引用:确保引用参数的生命周期覆盖使用范围
- 意外修改:正确使用const避免接口契约违反
- 重载困惑:避免传值与传引用的重载产生歧义
- 模板陷阱:注意模板推导中引用折叠的意外结果
最终建议: 将参数传递策略视为性能优化与代码清晰度的平衡艺术。培养"传递语义思维"------在设计每个函数接口时都思考:"这个参数会被修改吗?拷贝成本有多高?需要保持多态吗?" 这种思考方式是构建高效且正确C++代码的关键。
记住:在C++参数传递中,const引用通常是安全与效率的最佳平衡点,但了解例外情况同样重要。 条款20教会我们的不仅是一种技术选择,更是对C++对象模型深刻理解的体现。
深入解析:参数传递的核心机制
1. 问题根源:传值的隐藏代价
昂贵的拷贝构造示例:
cpp
#include <vector>
#include <string>
#include <iostream>
class ExpensiveToCopy {
public:
ExpensiveToCopy() {
data_.resize(1000); // 分配大量内存
std::cout << "默认构造" << std::endl;
}
ExpensiveToCopy(const ExpensiveToCopy& other)
: data_(other.data_) {
std::cout << "拷贝构造 - 成本高昂!" << std::endl;
}
ExpensiveToCopy& operator=(const ExpensiveToCopy& other) {
data_ = other.data_;
std::cout << "拷贝赋值 - 成本高昂!" << std::endl;
return *this;
}
~ExpensiveToCopy() {
std::cout << "析构" << std::endl;
}
private:
std::vector<int> data_;
};
// 糟糕的传值方式
void processByValue(ExpensiveToCopy obj) {
std::cout << "processByValue处理中..." << std::endl;
// 使用obj...
}
// 优秀的const引用方式
void processByConstRef(const ExpensiveToCopy& obj) {
std::cout << "processByConstRef处理中..." << std::endl;
// 使用obj,但不能修改
}
void demonstrate_copy_cost() {
ExpensiveToCopy expensive_obj;
std::cout << "=== 传值调用 ===" << std::endl;
processByValue(expensive_obj); // 触发拷贝构造 + 析构
std::cout << "=== const引用调用 ===" << std::endl;
processByConstRef(expensive_obj); // 零拷贝!
std::cout << "=== 临时对象传值 ===" << std::endl;
processByValue(ExpensiveToCopy()); // 默认构造 + 拷贝构造 + 析构×2!
}
对象切片问题示例:
cpp
#include <iostream>
#include <memory>
class Base {
public:
virtual ~Base() = default;
virtual void printType() const {
std::cout << "Base" << std::endl;
}
// 虚函数,需要多态行为
virtual std::unique_ptr<Base> clone() const {
return std::make_unique<Base>(*this);
}
};
class Derived : public Base {
public:
void printType() const override {
std::cout << "Derived" << std::endl;
}
std::unique_ptr<Base> clone() const override {
return std::make_unique<Derived>(*this);
}
void derivedOnlyMethod() const {
std::cout << "Derived特有方法" << std::endl;
}
};
// 错误的传值方式 - 导致切片
void processBaseByValue(Base base) {
base.printType(); // 总是输出"Base",丢失多态!
// 无法调用derivedOnlyMethod,即使传递的是Derived对象
// base.derivedOnlyMethod(); // 编译错误!
}
// 正确的const引用方式 - 保持多态
void processBaseByConstRef(const Base& base) {
base.printType(); // 正确输出实际类型!
// 可以安全地进行向下转型
if (auto derived = dynamic_cast<const Derived*>(&base)) {
derived->derivedOnlyMethod(); // 可以调用派生类方法
}
}
void demonstrate_slicing_problem() {
Derived derived_obj;
std::cout << "=== 传值导致切片 ===" << std::endl;
processBaseByValue(derived_obj); // 输出"Base" - 切片发生!
std::cout << "=== const引用保持多态 ===" << std::endl;
processBaseByConstRef(derived_obj); // 输出"Derived" - 多态保持!
std::cout << "=== 临时对象多态测试 ===" << std::endl;
processBaseByConstRef(Derived()); // 输出"Derived" - 仍然多态!
}
解决方案:const引用传递
1. 基本const引用模式
高效的对象传递:
cpp
#include <string>
#include <vector>
#include <algorithm>
class Customer {
public:
Customer(std::string name, int id)
: name_(std::move(name)), id_(id) {}
// const引用访问器
const std::string& getName() const { return name_; }
int getId() const { return id_; }
// 修改方法 - 非const引用返回
std::string& getName() { return name_; }
private:
std::string name_;
int id_;
};
// 优秀的const引用接口设计
class OrderProcessor {
public:
// 只读访问 - const引用
double calculateTotal(const Customer& customer,
const std::vector<double>& items) const {
double total = 0.0;
for (const auto& price : items) { // 容器也使用const引用
total += price;
}
// 可以读取customer但不能修改
std::cout << "为客户 " << customer.getName()
<< " 计算总额: " << total << std::endl;
return total;
}
// 需要修改对象 - 非const引用
void updateCustomerName(Customer& customer, const std::string& newName) {
customer.getName() = newName; // 调用非const版本
}
// 多个相关对象的const引用
bool validateOrder(const Customer& customer,
const std::vector<double>& items,
const std::string& paymentMethod) const {
if (items.empty()) {
std::cout << customer.getName() << " 的订单为空" << std::endl;
return false;
}
if (paymentMethod != "信用卡" && paymentMethod != "支付宝") {
std::cout << "不支持的支付方式: " << paymentMethod << std::endl;
return false;
}
return true;
}
};
// 标准库算法的const引用使用
void demonstrate_std_algorithm(const std::vector<Customer>& customers) {
// 查找特定客户 - 谓词使用const引用
auto it = std::find_if(customers.begin(), customers.end(),
[](const Customer& cust) { // const引用捕获
return cust.getName() == "张三";
});
if (it != customers.end()) {
std::cout << "找到客户: " << it->getName() << std::endl;
}
// 排序客户 - 比较器使用const引用
std::vector<Customer> sorted_customers = customers;
std::sort(sorted_customers.begin(), sorted_customers.end(),
[](const Customer& a, const Customer& b) {
return a.getId() < b.getId();
});
}
void demonstrate_const_reference() {
Customer customer("李四", 1001);
std::vector<double> items = {19.99, 29.99, 9.99};
OrderProcessor processor;
// const引用调用 - 高效且安全
double total = processor.calculateTotal(customer, items);
std::cout << "订单总额: " << total << std::endl;
// 验证订单
bool valid = processor.validateOrder(customer, items, "支付宝");
std::cout << "订单验证: " << (valid ? "通过" : "失败") << std::endl;
// 需要修改时使用非const引用
processor.updateCustomerName(customer, "王五");
std::cout << "更新后姓名: " << customer.getName() << std::endl;
// 容器操作演示
std::vector<Customer> customers = {
Customer("张三", 1002),
Customer("李四", 1001),
Customer("王五", 1003)
};
demonstrate_std_algorithm(customers);
}
2. 移动语义的现代方法
传值+移动的优化模式:
cpp
#include <string>
#include <utility>
class ModernStringProcessor {
private:
std::string data_;
public:
// 传统方式:const引用 + 拷贝
void setDataByConstRef(const std::string& data) {
data_ = data; // 拷贝赋值
}
// 改进方式:重载const引用和右值引用
void setDataOverloaded(const std::string& data) {
data_ = data; // 拷贝
}
void setDataOverloaded(std::string&& data) {
data_ = std::move(data); // 移动
}
// 现代方式:传值 + 移动(对于移动成本低的类型)
void setDataByValue(std::string data) {
data_ = std::move(data); // 移动赋值
}
const std::string& getData() const { return data_; }
};
// 资源管理类的移动优化
class ResourceHolder {
private:
std::vector<int> resources_;
public:
// 对于已知要存储的参数,传值可能更优
void addResource(std::vector<int> new_resources) {
if (resources_.empty()) {
resources_ = std::move(new_resources); // 移动
} else {
resources_.insert(resources_.end(),
new_resources.begin(), new_resources.end());
}
}
// 只读访问仍然使用const引用
bool containsResource(int value) const {
return std::find(resources_.begin(), resources_.end(), value)
!= resources_.end();
}
const std::vector<int>& getResources() const { return resources_; }
};
void demonstrate_move_semantics() {
ModernStringProcessor processor;
std::string large_data = "这是一个很长的字符串...";
std::cout << "=== 测试各种传递方式 ===" << std::endl;
// 左值传递测试
std::cout << "左值传递:" << std::endl;
processor.setDataByConstRef(large_data); // 拷贝
processor.setDataOverloaded(large_data); // 拷贝
processor.setDataByValue(large_data); // 拷贝
std::cout << "数据: " << processor.getData() << std::endl;
// 右值传递测试
std::cout << "右值传递:" << std::endl;
processor.setDataByConstRef("临时字符串"); // 构造 + 拷贝
processor.setDataOverloaded("临时字符串"); // 构造 + 移动
processor.setDataByValue("临时字符串"); // 构造 + 移动
std::cout << "数据: " << processor.getData() << std::endl;
// 显式移动测试
std::cout << "显式移动:" << std::endl;
std::string movable_data = "可移动数据";
processor.setDataByValue(std::move(movable_data)); // 移动
std::cout << "数据: " << processor.getData() << std::endl;
std::cout << "移动后源数据: " << movable_data << std::endl; // 有效但未指定状态
// 资源持有者测试
ResourceHolder holder;
std::vector<int> resources = {1, 2, 3, 4, 5};
holder.addResource(std::move(resources)); // 高效移动
std::cout << "资源数量: " << holder.getResources().size() << std::endl;
}
适用场景与例外情况
1. 内置类型与小型对象
传值更优的场景:
cpp
#include <cmath>
#include <type_traits>
// 内置类型 - 传值更高效
class MathUtils {
public:
// 内置类型直接传值
static double calculateDistance(double x1, double y1,
double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return std::sqrt(dx * dx + dy * dy);
}
// 小型的、拷贝成本低的结构体
struct Point {
double x, y;
// 平凡拷贝构造,传值可能更好
Point(double x = 0, double y = 0) : x(x), y(y) {}
};
// 对于小型Point,传值可能比传引用更高效
static Point midpoint(Point p1, Point p2) {
return Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
};
// 编译期类型特性判断
template<typename T>
void smartPass(T&& param) {
if constexpr (std::is_fundamental_v<std::decay_t<T>>) {
// 内置类型:可以考虑传值
processFundamental(std::forward<T>(param));
} else if constexpr (std::is_trivially_copyable_v<std::decay_t<T>> &&
sizeof(std::decay_t<T>) <= 2 * sizeof(void*)) {
// 小型平凡类型:可以考虑传值
processSmallTrivial(std::forward<T>(param));
} else {
// 其他类型:使用引用
processByReference(std::forward<T>(param));
}
}
// 接口设计的最佳实践
class Configuration {
public:
// 内置类型传值
void setTimeout(int milliseconds) {
timeout_ = milliseconds;
}
void setPrecision(double precision) {
precision_ = precision;
}
// 字符串使用const引用
void setName(const std::string& name) {
name_ = name;
}
// 或者使用传值+移动
void setDescription(std::string description) {
description_ = std::move(description);
}
// 返回const引用避免拷贝
const std::string& getName() const { return name_; }
const std::string& getDescription() const { return description_; }
private:
int timeout_ = 1000;
double precision_ = 0.001;
std::string name_;
std::string description_;
};
void demonstrate_appropriate_usage() {
MathUtils::Point p1(1.0, 2.0);
MathUtils::Point p2(4.0, 6.0);
// 小型对象传值 - 可能更高效
auto mid = MathUtils::midpoint(p1, p2);
std::cout << "中点: (" << mid.x << ", " << mid.y << ")" << std::endl;
// 内置类型传值
double dist = MathUtils::calculateDistance(0, 0, 3, 4);
std::cout << "距离: " << dist << std::endl;
// 配置对象使用
Configuration config;
config.setTimeout(5000); // 内置类型传值
config.setPrecision(0.0001); // 内置类型传值
config.setName("高性能服务器"); // const引用
config.setDescription("这是服务器的详细配置描述"); // 传值+移动
std::cout << "配置名称: " << config.getName() << std::endl;
}
2. 标准库容器的传递策略
容器参数的最佳实践:
cpp
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
class DataProcessor {
public:
// 只读访问容器 - const引用
static double calculateAverage(const std::vector<double>& data) {
if (data.empty()) return 0.0;
double sum = 0.0;
for (const auto& value : data) { // const引用遍历
sum += value;
}
return sum / data.size();
}
// 需要修改容器 - 非const引用
static void normalizeData(std::vector<double>& data) {
if (data.empty()) return;
double avg = calculateAverage(data);
for (auto& value : data) { // 非const引用修改
value -= avg;
}
}
// 创建新容器 - 考虑返回值优化
static std::vector<double> filterPositive(const std::vector<double>& data) {
std::vector<double> result;
std::copy_if(data.begin(), data.end(), std::back_inserter(result),
[](double value) { return value > 0; });
return result; // 依赖RVO
}
// 大型容器的只读处理 - 始终使用const引用
static std::string concatenateAll(const std::vector<std::string>& strings) {
std::string result;
for (const auto& str : strings) {
result += str;
}
return result;
}
};
// 模板容器算法
template<typename Container>
auto findMaxValue(const Container& container) -> typename Container::value_type {
if (container.empty()) {
throw std::invalid_argument("容器为空");
}
auto it = std::max_element(container.begin(), container.end());
return *it; // 返回值的拷贝(假设value_type较小)
}
// 对于可能很大的返回值,提供引用版本
template<typename Container>
auto findMaxElement(const Container& container) -> const typename Container::value_type& {
if (container.empty()) {
throw std::invalid_argument("容器为空");
}
auto it = std::max_element(container.begin(), container.end());
return *it; // 返回const引用
}
void demonstrate_container_passing() {
std::vector<double> data = {1.5, -2.3, 3.7, -0.8, 4.1};
std::vector<std::string> strings = {"Hello", " ", "World", "!"};
// 只读处理 - const引用
double avg = DataProcessor::calculateAverage(data);
std::cout << "平均值: " << avg << std::endl;
// 修改处理 - 非const引用
DataProcessor::normalizeData(data);
std::cout << "标准化后平均值: " << DataProcessor::calculateAverage(data) << std::endl;
// 创建新容器 - 依赖RVO
auto positive_data = DataProcessor::filterPositive(data);
std::cout << "正数数量: " << positive_data.size() << std::endl;
// 字符串拼接 - const引用避免拷贝
std::string concatenated = DataProcessor::concatenateAll(strings);
std::cout << "拼接结果: " << concatenated << std::endl;
// 模板算法使用
double max_val = findMaxValue(data);
const auto& max_ref = findMaxElement(data); // 避免拷贝
std::cout << "最大值: " << max_val << " (引用: " << max_ref << ")" << std::endl;
}
高级技巧与陷阱防范
1. 完美转发与通用引用
现代C++的转发技术:
cpp
#include <utility>
#include <type_traits>
class AdvancedProcessor {
private:
std::string stored_value_;
public:
// 通用引用 + 完美转发
template<typename T>
void setValue(T&& value) {
stored_value_ = std::forward<T>(value);
std::cout << "设置值: " << stored_value_;
if constexpr (std::is_lvalue_reference_v<T&&>) {
std::cout << " (左值)" << std::endl;
} else {
std::cout << " (右值)" << std::endl;
}
}
// 带约束的通用引用
template<typename T>
std::enable_if_t<std::is_constructible_v<std::string, T>>
setValueConstrained(T&& value) {
stored_value_ = std::forward<T>(value);
}
const std::string& getValue() const { return stored_value_; }
};
// 工厂函数中的完美转发
template<typename T, typename... Args>
std::unique_ptr<T> createObject(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
class ExampleClass {
public:
ExampleClass(const std::string& name, int value)
: name_(name), value_(value) {
std::cout << "ExampleClass构造: " << name_ << std::endl;
}
ExampleClass(std::string&& name, int value)
: name_(std::move(name)), value_(value) {
std::cout << "ExampleClass移动构造: " << name_ << std::endl;
}
private:
std::string name_;
int value_;
};
void demonstrate_perfect_forwarding() {
AdvancedProcessor processor;
std::string name = "测试名称";
std::cout << "=== 完美转发演示 ===" << std::endl;
// 左值传递
processor.setValue(name); // 拷贝
std::cout << "原值: " << name << std::endl;
// 右值传递
processor.setValue("临时字符串"); // 移动
processor.setValue(std::move(name)); // 移动
std::cout << "移动后原值: " << name << std::endl; // 有效但未指定状态
// 工厂函数使用
auto obj1 = createObject<ExampleClass>("对象1", 100); // 可能移动构造
auto obj2 = createObject<ExampleClass>(name, 200); // 拷贝构造
std::cout << "=== 带约束的转发 ===" << std::endl;
// 只有能构造std::string的类型才被接受
processor.setValueConstrained("字符串字面量"); // OK
processor.setValueConstrained(std::string("临时")); // OK
// processor.setValueConstrained(42); // 编译错误!不能从int构造string
}
2. 生命周期管理与陷阱防范
悬空引用预防:
cpp
#include <memory>
#include <functional>
class DangerousReferences {
public:
// 危险的接口:返回内部数据的引用
const std::string& getData() const { return data_; }
// 更安全的接口:返回拷贝或共享指针
std::string getDataCopy() const { return data_; }
std::shared_ptr<const std::string> getDataShared() const {
return std::make_shared<const std::string>(data_);
}
void setData(const std::string& data) { data_ = data; }
private:
std::string data_ = "默认数据";
};
// 悬空引用的典型场景
class CallbackManager {
private:
std::function<void(const std::string&)> callback_;
public:
void setCallback(const std::function<void(const std::string&)>& callback) {
callback_ = callback;
}
void processData(const std::string& input) {
if (callback_) {
callback_(input);
}
}
};
void demonstrate_dangling_reference() {
std::cout << "=== 悬空引用演示 ===" << std::endl;
DangerousReferences obj;
// 安全的用法
std::string safe_copy = obj.getDataCopy();
auto shared_data = obj.getDataShared();
// 潜在的危险用法
const std::string& dangerous_ref = obj.getData();
obj.setData("新数据"); // dangerous_ref可能悬空!
std::cout << "安全拷贝: " << safe_copy << std::endl;
std::cout << "共享数据: " << *shared_data << std::endl;
std::cout << "危险引用: " << dangerous_ref << std::endl; // 可能有问题!
// 回调中的生命周期问题
CallbackManager manager;
{
std::string local_data = "局部数据";
// 捕获局部变量的引用 - 危险!
manager.setCallback([&](const std::string& input) {
std::cout << "回调处理: " << local_data << " + " << input << std::endl;
});
manager.processData("输入数据"); // 此时还安全
} // local_data离开作用域,被销毁
// 此时回调中的local_data已经悬空!
// manager.processData("再次输入"); // 未定义行为!
std::cout << "=== 安全的回调设计 ===" << std::endl;
// 安全的回调:通过值捕获或共享所有权
auto safe_data = std::make_shared<std::string>("安全数据");
manager.setCallback([safe_data](const std::string& input) {
std::cout << "安全回调: " << *safe_data << " + " << input << std::endl;
});
manager.processData("安全输入"); // 始终安全
}
// 返回局部对象引用的错误模式
class WrongFactory {
public:
// 错误:返回局部对象的引用
const std::string& createBadString() {
std::string local = "局部字符串";
return local; // 返回悬空引用!
}
// 正确:返回值
std::string createGoodString() {
return "安全字符串"; // 返回值优化
}
// 正确:返回静态对象的引用
const std::string& getStaticString() {
static std::string static_str = "静态字符串";
return static_str; // 安全:静态对象生命周期到程序结束
}
};
void demonstrate_lifetime_management() {
WrongFactory factory;
// 危险的调用
// const auto& bad_ref = factory.createBadString(); // 悬空引用!
// std::cout << bad_ref << std::endl; // 未定义行为
// 安全的调用
auto good_str = factory.createGoodString(); // 拷贝或移动
const auto& static_ref = factory.getStaticString(); // 静态引用安全
std::cout << "好字符串: " << good_str << std::endl;
std::cout << "静态引用: " << static_ref << std::endl;
}
性能测试与权衡决策
1. 实际性能测量
基准测试示例:
cpp
#include <chrono>
#include <vector>
#include <string>
class PerformanceTest {
public:
struct LargeData {
std::vector<int> data;
LargeData() {
data.resize(10000, 42); // 大量数据
}
// 拷贝成本很高
LargeData(const LargeData& other) : data(other.data) {
// 模拟昂贵的拷贝
}
};
// 传值版本
static void processByValue(LargeData data) {
// 一些处理...
volatile int sum = 0;
for (int val : data.data) {
sum += val;
}
}
// const引用版本
static void processByConstRef(const LargeData& data) {
// 相同的处理...
volatile int sum = 0;
for (int val : data.data) {
sum += val;
}
}
static void runBenchmark() {
LargeData test_data;
const int iterations = 1000;
std::cout << "=== 性能基准测试 ===" << std::endl;
std::cout << "数据大小: " << sizeof(test_data) << " 字节" << std::endl;
std::cout << "迭代次数: " << iterations << std::endl;
// 测试传值性能
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
processByValue(test_data);
}
auto end = std::chrono::high_resolution_clock::now();
auto value_duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
// 测试const引用性能
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
processByConstRef(test_data);
}
end = std::chrono::high_resolution_clock::now();
auto ref_duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "传值耗时: " << value_duration.count() << " μs" << std::endl;
std::cout << "引用耗时: " << ref_duration.count() << " μs" << std::endl;
std::cout << "性能提升: "
<< (value_duration.count() - ref_duration.count()) * 100.0 / value_duration.count()
<< "%" << std::endl;
}
};
// 小型对象的性能测试
class SmallObjectTest {
public:
struct SmallData {
int x, y, z;
// 小型平凡类型
};
static void processSmallByValue(SmallData data) {
volatile int result = data.x + data.y + data.z;
}
static void processSmallByConstRef(const SmallData& data) {
volatile int result = data.x + data.y + data.z;
}
static void runBenchmark() {
SmallData small_data{1, 2, 3};
const int iterations = 1000000; // 更多迭代以显示差异
std::cout << "\n=== 小型对象性能测试 ===" << std::endl;
std::cout << "数据大小: " << sizeof(small_data) << " 字节" << std::endl;
std::cout << "迭代次数: " << iterations << std::endl;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
processSmallByValue(small_data);
}
auto end = std::chrono::high_resolution_clock::now();
auto value_duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
processSmallByConstRef(small_data);
}
end = std::chrono::high_resolution_clock::now();
auto ref_duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "传值耗时: " << value_duration.count() << " μs" << std::endl;
std::cout << "引用耗时: " << ref_duration.count() << " μs" << std::endl;
if (value_duration.count() < ref_duration.count()) {
std::cout << "传值更快,优势: "
<< (ref_duration.count() - value_duration.count()) * 100.0 / ref_duration.count()
<< "%" << std::endl;
} else {
std::cout << "引用更快,优势: "
<< (value_duration.count() - ref_duration.count()) * 100.0 / value_duration.count()
<< "%" << std::endl;
}
}
};
void demonstrate_performance_measurement() {
PerformanceTest::runBenchmark();
SmallObjectTest::runBenchmark();
}
2. 实际应用总结
最终决策框架:
cpp
#include <type_traits>
// 编译期传递策略选择
template<typename T>
struct PassStrategy {
// 默认策略:对于未知类型使用const引用
using type = const T&;
};
// 特化:内置类型使用传值
template<typename T>
struct PassStrategy<T, std::enable_if_t<std::is_fundamental_v<T>>> {
using type = T;
};
// 特化:小型平凡类型使用传值
template<typename T>
struct PassStrategy<T, std::enable_if_t<
std::is_trivially_copyable_v<T> &&
sizeof(T) <= 2 * sizeof(void*) &&
!std::is_fundamental_v<T>>> {
using type = T;
};
// 特化:移动成本低的类型考虑传值
template<typename T>
struct PassStrategy<T, std::enable_if_t<
std::is_nothrow_move_constructible_v<T> &&
sizeof(T) <= 4 * sizeof(void*)>> {
using type = T; // 或者保持const T&,根据性能测试决定
};
// 使用策略的模板函数
template<typename T>
void processWithStrategy(typename PassStrategy<T>::type param) {
// 使用param...
}
// 实际工程中的经验法则
class PracticalGuidelines {
public:
// 经验法则1:内置类型传值
void setIntParam(int value) { /* ... */ }
void setDoubleParam(double value) { /* ... */ }
void setBoolParam(bool value) { /* ... */ }
// 经验法则2:迭代器传值(通常很小)
void processRange(std::vector<int>::iterator begin,
std::vector<int>::iterator end) { /* ... */ }
// 经验法则3:STL容器使用const引用
void processVector(const std::vector<int>& vec) { /* ... */ }
void processString(const std::string& str) { /* ... */ }
// 经验法则4:自定义大对象使用const引用
void processLargeObject(const LargeData& obj) { /* ... */ }
// 经验法则5:需要存储的参数考虑传值+移动
void storeString(std::string str) {
stored_string_ = std::move(str);
}
// 经验法则6:多态基类必须使用引用或指针
void processPolymorphic(const Base& obj) { /* ... */ }
private:
std::string stored_string_;
};
void demonstrate_practical_decision() {
PracticalGuidelines guidelines;
// 遵循经验法则的调用
guidelines.setIntParam(42); // 内置类型传值
guidelines.setDoubleParam(3.14); // 内置类型传值
std::vector<int> numbers = {1, 2, 3, 4, 5};
guidelines.processRange(numbers.begin(), numbers.end()); // 迭代器传值
guidelines.processVector(numbers); // 容器const引用
std::string name = "测试名称";
guidelines.processString(name); // 字符串const引用
guidelines.storeString("要存储的字符串"); // 传值+移动
// 多态使用
Derived derived_obj;
guidelines.processPolymorphic(derived_obj); // 基类const引用
std::cout << "所有调用都遵循参数传递的最佳实践" << std::endl;
}
// 最终建议的总结实现
class FinalRecommendation {
public:
/*
* 参数传递决策树:
*
* 1. 参数会被修改吗?
* - 是:使用非const引用 (void modify(T& param))
* - 否:继续判断
*
* 2. 参数是内置类型或很小吗?(sizeof(T) <= 2*sizeof(void*))
* - 是:考虑传值 (void process(T param))
* - 否:继续判断
*
* 3. 需要多态行为吗?
* - 是:使用const引用 (void process(const Base& param))
* - 否:继续判断
*
* 4. 参数会被存储吗?
* - 是:考虑传值+移动 (void store(T param) { member_ = std::move(param); })
* - 否:使用const引用 (void process(const T& param))
*
* 5. 在性能关键路径上吗?
* - 是:进行基准测试,根据结果决定
* - 否:使用const引用(安全的默认选择)
*/
// 安全的默认选择
template<typename T>
void safeDefault(const T& param) {
// 大多数情况的正确选择
}
// 性能优化后的选择
template<typename T>
void optimizedChoice(typename PassStrategy<T>::type param) {
// 基于实际测量的优化选择
}
};
int main() {
demonstrate_copy_cost();
demonstrate_slicing_problem();
demonstrate_const_reference();
demonstrate_move_semantics();
demonstrate_appropriate_usage();
demonstrate_container_passing();
demonstrate_perfect_forwarding();
demonstrate_dangling_reference();
demonstrate_lifetime_management();
demonstrate_performance_measurement();
demonstrate_practical_decision();
return 0;
}