Tip of the Week 5 消逝的演出
有时候,为了正确的运用C++的库,你既需要理解库本身,又需要理解这门语言。那么……下面的代码中的问题是什么?
1 | // 别这么干 |
s1+s2
和absl::StrCat(s1, s2)
都创建了临时对象(这里都是字符串对象,但同样的规则使用于任意对象)。成员函数c_str()
返回指向底层数据的指针,而底层数据与临时对象生命周期一致。临时对象能活多长?根据C++17标准中的[class temporary]
,“在临时对象创建点所在的完整表达式中,临时变量的销毁时表达式的最后一步。”,那么也就是说,当赋值运算符右边的表达式结束的时候,临时变量就被销毁了,c_str()
的返回值就成了“野”指针。那么如何避免这类问题呢?
方法一,在完整表达式结束前用完临时对象:
1 | // 安全(虽然弱鸡了一点) |
方法2,存储临时对象:
既然你都(在栈上)创建对象了,干嘛不多留他一会呢?这可能比他初看上去便宜。因为一个叫“返回值优化”的玩意儿,临时变量会在赋值目标对象上直接构造,而不是复制:
1 | // 安全(且比你想象的更高效) |
方法3,存储一个指向临时变量的引用:
C++标准[class temporary]
:“若临时变量绑定到引用,或临时变量的子对象绑定到引用,则临时变量声明周期会被延长到与该引用一致。”
因为返回值优化的存在,这种方式常并不比存储对象背身(方法2)更“便宜”,而且还有可能给人整蒙圈。
1 | // 同等安全: |
方法4,设计函数的时候就别返回对象???
很多函数遵循这条原则;但也有很多函数不遵守。相比要求调用者传进一个指向输出参数的指针,有时候返回个对象真的更好。在创建临时对象的地方多加小心,在操作临时对象的时候,任何返回对象内部的指针或者引用的东西都有可能出问题。c_str()
是最明显的罪魁祸首,但是protobuf的(或其他的可修改)访问器(getter)和其他通常的访问器也同样有可能出问题。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 我是Android开发者!