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不友好
  • 建议在性能要求不高的地方可以使用

在高性能场景怎么传递呢

  1. 模板方式
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的移动构造

模板方式的优缺点

  • 优点可以适应各种情况
  • 缺点失去了强制类型检查
  1. 完美转发
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&, 因为

  1. initializer_list非常小引用和传值相当
  2. initializer_list底层是const T[N]本身是const的
    但是写成const initializer_list&看着舒服也没有什么额外开销 😃
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++