C++中的参数传递
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
参数传递在程序中非常普遍数量也非常多。有时候看似一点点开销但是积少成多。下面是不同情况下传递参数的一些总结。
普通参数
class Widget { ... };
void fun(const Widget* const widget); // 可以为nullptr的只读参数第一个const表示widget本身不会被修改第二表示widget这个指针只读
void fun(const Widget* const widget); // 可以为nullptr可以被修改
void fun(Widget& widget); // 不为nullptr可被修改
void fun(const Widget& widget); // 不为nullptr只读
希望保存传入的参数
class Test {
public:
void fun(Widget widget) { // fun收到了左值会有一次拷贝构造一次移动赋值如果fun收到了右值会有一次移动构造一次移动赋值综合这样写效率最高。
_widget = std::move(widget);
}
private:
Widget _widget;
};
unique_ptr
void fun(std::unique_ptr<T> obj); // 表明fun方法希望获取T的所有权
如果不需要传递所有权应该按照普通数据结构来传递不要直接传递unique_ptr
auto obj = std::make_unique<std::string>();
// void fun(const std::string* const str); // 读string
// void fun(std::string* const str); // 写string
fun(obj.get());
// void fun(const std::string& str); // 读string
// void fun(std::string& str); // 写string
fun(*obj);
在工厂模式场景可以传递unique_ptr
void factory(std::unique_ptr<std::string>& obj) {
obj = std::make_unique<std::string>();
}
shared_ptr
void fun(std::shared_ptr<T> obj); // fun希望获取obj的所有权会导致shared_ptr的引用计数加一他是原子操作是一个低效的操作
如果不需要分享所有权应该按照普通数据结构来传递不要直接传递shared_ptr
Lambda 表达式
lambda的底层实现
捕获值
int x = 0;
auto func = [=](int i)->bool { return i > x; };
// 编译器实现
struct lambda_xyz { // 编译器自动生成一个唯一的名字
lambda_xyz(int x) : _x(x) {} // 捕获值时构造lambda_xyz时以值传递
bool operator()(int i) const { return i > _x; }
int _x;
};
捕获引用
int x = 0;
auto func = [&](int i)->bool { return i > x; };
// 编译器实现
struct lambda_xyz { // 编译器自动生成一个唯一的名字
lambda_xyz(int& x) : _x(x) {} // 捕获引用时构造lambda_xyz时以引用传递
bool operator()(int i) const { return i > _x; }
int& _x;
};
以值传递方式传递lambda表达式
void bar(std::function<bool(int)> func) { // 由于function是类所以会调用他的拷贝构造函数
bool res = func(100);
}
void bar(const std::function<bool(int)>& func) { // 如果std::function提前已经构造好了const引用方式效率会更高一些
bool res = func(100);
}
void run() {
int x = 0;
auto func = [=](int i)->bool { return i > x; };
bar(func);
}
用std::function传递lambda表达式的优缺点
- 优点有明确的参数类型和返回值类型编译器会强制检查
- 缺点有内存开销对特别小的lambda不友好
- 建议在性能要求不高的地方可以使用
在高性能场景怎么传递呢
- 模板方式
template <typename T>
void bar(T func) {
bool res = func(5);
}
int x = 0;
auto func = [=](int i)->bool { return i > x; };
bar(func); // 调用lambda的拷贝构造
bar(std::move(func)); // 调用lambda的移动构造
模板方式的优缺点
- 优点可以适应各种情况
- 缺点失去了强制类型检查
- 完美转发
template <typename T>
void bar(T&& func) {
bool res = func(5);
}
int x = 0;
auto func = [=](int i)->bool { return i > x; };
bar(func); // 左值引用的方式
bar(std::move(func)); // 右值引用方式
完美转发方式的优缺点
- 优点没有内存分配的开销也没有额外的拷贝
- 缺点失去了强制类型检查另外都是引用方式传递
传递initializer_list
例子
void fun(std::initializer_list<int> array);
fun({1, 2, 3});
initializer_list不需要const initializer_list&, 因为
- initializer_list非常小引用和传值相当
- initializer_list底层是const T[N]本身是const的
但是写成const initializer_list&看着舒服也没有什么额外开销 😃