More Effective C++读书笔记:条款20

Aki 发布于 2022-10-18 244 次阅读


20协助完成“返回值优化(RVO)”:

RVO:return value optimization 返回值优化
利用函数的return点消除一个局部临时对象

防止函数返回对象

  • 既然函数返回对象无法避免临时对象的产生,那就防止函数返回对象(改为返回指针或引用)
  • 但是此方法易产生危险或者不合理的行为
    • 例如:返回指针就要考虑是否应该删除此函数返回指针,删除了易造成内存泄漏(因为常被忽略或者遗忘)。
    • 例如:返回引用,这个引用指向局部对象,局部对象返回时自动被销毁,引用却指向一个不在存货的对象。

// 错误写法
const Rational& operator*(const Rational & lhs, const Rational & rhs) 
 {
    Rational res(lhs.a() * rhs a(), lhs.b() * rhs.b());
    return res;
}

函数如果返回对象

  • 函数如果返回对象,因为以值传递的方式返回对象,背后隐层的构造和析构成本都是无法消除的。
  • 从效率的眼光:我们不应该在乎函数返回了一个对象,应该在乎那个对象的成本,如果成本为0,生产多少个都无所谓

RVO(返回值优化)

  • 通过特殊的写法,使编译器消除临时对象的成本,返回所谓的构造参数以取代对象
const Rational operator*(const Rational & lhs, const Rational & rhs) 
 {
    return Rational(lhs.a() * rhs a(), lhs.b() * rhs.b());
}

写法看似产生一个Rational临时对象并为它的构造和析构付出代价,函数复制此临时对象作为返回值又付出了构造和析构的代价,但是其实编译器将临时对象优化,使他们全都不存在。编译器优化过程:将return表达式所定义的对象构造于一个命名对象c的内存中,也就是说没有任何临时对象需要被产生出来,取而代之你只需要一个构造产生c的代价。最优写法:使用inline消除调用函数的额外开销

incline const Rational operator*(const Rational & lhs, const Rational & rhs) 
 {
    return Rational(lhs.a() * rhs a(), lhs.b() * rhs.b());
}

C++还有一个移动构造函数,专门用来对临时值,将亡值的处理。。。。。。

class A
{
public:
	A() { cout << "构造" << endl; }
	A(const A& rhs) { cout << "构造" << endl; }
	A(A&& rhs)noexcept { cout << "移动构造" << endl; }
	~A()noexcept { cout << "析构" << endl; }
};

const A func()
{
	A a;        //a是一个临时对象
	return a;  //没有优化处理,先构造a,然后移动构造,析构两次
}

const A func2()
{
	return A();   //返回值优化处理,构造一次,析构一次,这是一个无名对象
}