C++读书笔记

超精简Effective Modern C++ 第八章 微调

条款41.针对可复制的形参,在移动成本低并且一定会被复制的前提下,考虑按值传递

  • 对于可以复制,在移动成本低的一定会被复制的形参而言,按值传递可能会和按引用传递有差不多的效率,而且不需要写更多代码
  • 通过构造复制形参的成本,可能比赋值赋值形参高很多
  • 按值传递肯定会有切片问题,所以这里值传递不适合基类
// 三种传值的方式

// 通过重载的方式传递,需要维护两个函数
// 成本:左值=一次复制 右值=一次移动
class Widget{
public:
	void addName(const std::string& newName)
	{ names.push_back(newName); }
	
	void addName(std::string&& newName)
	{ names.push_back(newName); }
}

// 使用万能引用,需要写到头文件
// 成本:左值=一次复制 右值=一次移动
class Widget{
public:
	template<typename T>
	void addName(T&& newName)
	{ names.push_back(std::forward<T>(newName)); }
}

// 按值传递
// 成本:无论是左值还是右值都存在一次额外移动
class Widget{
public:
	void addName(std::string newName)
	{ names.push_back(std::move(newName)); }
}

条款42.考虑置入而非插入

使用emplace_back而非push_back

使用push_back时

vs.push_back("xyzzy")
// 将被看做
vs.push_back(std::string("xyzzy"))

首先会产生临时对象,临时对象传递给push_back的右值重载版本(一次移动),然后在std::vector中创建一个新的对象(第二次构造),最后临时对象被析构。

而使用emplace_back时实际上传入的是构造参数。

如果在以下情况度成立的情况,置入效率一定更高:

  • 添加的值是以构造而非赋值的方式加入容器
  • 传递的实参类型和容器的持有类型不同
  • 容器不太可能因为重复情况而拒绝新值,例如set,如果拒绝则浪费一次构造和析构

与资源管理一起用的情况

在使用shared_ptr的情况下使用push_back可能成本会超过预期,而如果使用emplace_back可能导致异常发生的时候产生泄漏。

我们任何时候都应该先定义shared_ptr然后传入容器中:

std::shared_ptr<Widget> spw(new Widget, killWidget);
ptrs.push_back(std::move(spw))

std::shared_ptr<Widget> spw(new Widget, killWidget)
ptrs.emplace_ptr(std::move(spw))

传入空指针的陷阱

regexes.emplace_back(nullptr)//不会报错
regexes.push_back()//报错

因为emplace_back传入的是构造参数,所以实际上是允许空指针的,一些用explicit拒绝的类型,在这种情况下也被允许传入,可能会发生问题。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注