C++中的参数传递

参数传递在程序中非常普遍,数量也非常多。有时候看似一点点开销,但是积少成多。下面是不同情况下传递参数的一些总结。

普通参数

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&看着舒服,也没有什么额外开销 😃
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值