[C++] 编程实践之1: Google的C++代码风格4:来自Google的奇技

时间:2022-04-17 07:43:12

来自Google的奇技

  Google用了很多自己实现的技巧/工具使C++代码更加简装,我们使用C++的方式可能和你在气他地方见到的有所不同。

所有权与智能指针

动态分配出的对象最好有单一且固定的所有主(owner),且通过智能指针传递所有权(ownership)。

定义:

  • 所有权是一种登记/管理动态内存和其它资源的技术。动态分配出的对象的所有主是一个对象或者函数,后者负责确保当前者无用时就自动销毁前者。所有权有时候可以共享,那么就由最后一个所有主来负责销毁它。甚至也可以不同共享,在代码中直接把所有权传递给其它对象。
  • 其实您可以把智能指针当成一个重载了*和->的“对象”来看。智能指针类型被用来自动化所有权的登记工作,来确保执行销毁义务到位。std::unique_ptr是C++11新推出的一种智能指针类型,用来表示动态分配出的对象的“独一无二”的所有权;当std::unique_ptr离开作用域,对象就会被销毁。不能复制std::unique_ptr,但是可以把它移动给新所有主。std::shared_ptr同样表示动态分配对象的所有权,但是可以被共享,也可以被复制;对象的所有权由所有复制者共同拥有,最有一个复制者被销毁时,对象也会随着被销毁。

优点:

  • 如果没有清晰、逻辑条理所有权安排,不可能管理好动态分配的内存。
  • 传递对象的所有权,其开销比复制来的小,如果可以复制的话。
  • 传递所有权也比“借用”指针或者引用来的简单,毕竟它大大省去了两个用户一起协调对象生命周期的工作。
  • 如果所有权逻辑有条理,有文档且不乱来的话,可读性很棒。
  • 可以不用手动完成所有权的登记工作,大大简化了代码,也免去了一大波错误之烦恼。
  • 对于const对象来说,智能指针简单易用,也比深度复制高效。

缺点:

  • 不得不用指针(不管是智能指针还是原生指针)来表示和传递所有权。指针予以可要比值语义复杂得多了,特别实在API里:你不管要操心所有权,还要顾及别名,生命周期,可变性(mutability)以及其它大大小小的问题。
  • 其实值语义的开销进场被高估,所以就所有权的性能来说,可不能光只考虑可读性以及复杂性。
  • 如果API依赖所有权的传递,就会害得客户端不得不用单一的内存管理模型。
  • 销毁资源并回收的相关代码不是很明朗。
  • std::unique_ptr的所有关传递原理是C++11的move语法,后者毕竟是刚刚刚推出的,容易迷惑程序员。
  • 如果原本的所有权设计已经够完善了,那么若要引入所有权共享机制,可能不得不重构整个系统。
  • 所有关共享机制的登记工作在运行时进行,开销可能相当不小。
  • 某些极端情况下,所有权被共享的对象永远不会被销毁,比如引用死循环(cyclic reference)。
  • 智能指针并不能够替代原生指针。

决定:

  • 如果必须使用动态分配,倾向于保持分配者的所有权。如果其他地方要使用这个对象,最好传递它的拷贝,或者传递一个不用改变所有权的指针或者引用。倾向于使用std::unique_ptr来明确所有权传递,例如:
std::unique_ptr<Foo> FooFactory();
void FooConsumer(std::unique_ptr<Foo> ptr);

避免使用共享所有权。如果对性能要求很高,并且操作的对象时不可变的(比如说std::shared_ptr),这时可以用共享所有权来避免昂贵的拷贝操作。如果确实要使用共享所有权,倾向于使用std::shared_ptr。

  • 不要在新代码中使用scoped_ptr ”,除非你必须兼容老版本的C++,总是用std::unique_ptr代替std::auto_ptr。

cpplint

使用cpplint.py检查风格错误。

  cpplint.py是一个用来分析源文件,能够检查出多种风格错误的工具。它并不完美,甚至还会漏报和误报,但它仍然是一个非常有用的工具。在行尾加 // NOLINT,或者上一行加 // NOLINTNEXTLINE,可以忽略报错。
  某些项目会指导你如何使用他们的项目工具运行cpplint.py。如果你参与的项目没有提供,你可以单独下载cpplint.py

总结

  1. 把智能指针当成对象来看待的话,就很好领会它与所指对象之间的关系了。
  2. 原来Rust的Ownership思想是受到了C++智能指针的很大启发。
  3. scoped_ptr和auto_ptr已经过时,现在是shared_ptr和unique_ptr的天下了。
  4. 按本文来说,似乎除了智能指针,还有气他所有权机制,值得留意。
  5. Arch Linux用户注意了,AUR有对cpplint打包。