《谷歌C++编程风格指导》笔记

来源:互联网 发布:人工智能性爱系统 编辑:程序博客网 时间:2024/06/05 08:25

3.10. 存取控制

原文:Make data members private, and provide access to them through accessor functions as needed (for technical reasons, we allow data members of a test fixture class to be protected when using Google Test). Typically a variable would be calledfoo_ and the accessor function foo(). You may also want a mutator function set_foo(). Exception: static const data members (typically called kFoo) need not be private。 The definitions of accessors are usually inlined in the header file.

疑惑: 所有成员数据声明为private, 意味着成员数据不再是一个方便基础的属性, 会给子类带来很多的不便吧。

4.1. 智能指针

原文:Prefer to have single, fixed owners for dynamically allocated objects. Prefer to transfer ownership with smart pointers
要点: 
1. 如果确实要用只能指针的话, std::unique_ptr 完成胜任。 在这种情况下, 只有你需要和老代码兼容时才用scoped_ptr
2. 允许指针宿主(ownnership)可变通常不是一个好主意, 在极特殊情况下如果你一定要这样设计, 建议使用 shared_prt
3. 绝对不要使用linked_ptr 和 std::auto_ptr
疑惑:
里面提到了五中智能指针 unique_ptr, scoped_ptr, shared_ptr, linked_ptr, auto_ptr.   分别从特点, 应用场景和性能损耗去学习。

5.2.  Rvalue References

原文:Do not use rvalue references, std::forward, std::move_iterator, or std::move_if_noexcept. Use the single-argument form of std::move only with non-copyable arguments.

疑惑: 对Rvalue Reference 一无所知。 留个坑, 贴两篇博客回头一起看
http://www.artima.com/cppsource/rvalue.html
http://blog.csdn.net/thesys/article/details/5651713

5.3.  缺省(默认)参数

原文:We do not allow default function parameters, except in limited situations as explained below. Simulate them with function overloading instead, if appropriate.
要点:默认参数的优点是使代码简洁。 但有两个明显的缺点;1. 函数指针定义的形式会和调用的形式不太一样, 造成误解。 2. 将默认参数定义在头文件, 头文件可能会被拷贝到很多的地方, 将来升级需要修改缺省值时会很麻烦。 而如果用函数重载代替缺省参数, 默认值就只与实现相关。
两个例外:1. 函数是局部的(无论是显示的定位为静态的或者是位于匿名名字空间(unamed namespace)中, 因为只是局部使用, 一般不会有上面的两个问题。 2. 用默认参数来模拟变长参数(如下)
// Support up to 4 params by using a default empty AlphaNum.string StrCat(const AlphaNum &a,              const AlphaNum &b = gEmptyAlphaNum,              const AlphaNum &c = gEmptyAlphaNum,              const AlphaNum &d = gEmptyAlphaNum);


5.4.  变长数组和alloca()
原文:We do not allow variable-length arrays or alloca().
优点: 变长数组和alloca()都有自然的语义, 并且也很高效。
缺点:首先他们都没有在标准c++里。 更重要的是, 他们都是直接依赖于栈空间大小,会引发很多类似于“在我的测试环境上明明运行良好, 已上线就跑飞了”的问题, 这类问题往往还不容易定位。
结论: 推荐使用更安全的scoped_ptr/scoped_array
疑惑: 对变长数组和堆上的动态内存分配alloca()不了解。 可参考阅读:http://blog.csdn.net/masefee/article/details/6835688

5.4.  运行时类型识别(RTTI)
原文:RTTI has legitimate uses but is prone to abuse, so you must be careful when using it. You may use it freely in unittests, but avoid it when possible in other code. In particular, think twice before using RTTI in new code.
缺点: RTTI代码难于维护和扩展,如果你需要在运行期间确定一个对象的类型, 这通常说明你需要考虑重新设计你的类,这样的问题同样适用于你自己开发一个类似于RTTI的功能。当你一定要使用RTTI时, 可从以下两个方向去寻找替代方案:
1. 如果能将行为置放于对象内部实现, 那么用虚函数取代RTTI是最好的选择(既实现了不同的类有不同的行为, 又避免了RTTI 的问题)
2. 如果不能将行为置于对象内部实现,考虑使用双重分派方案, 如访问者模式, 来取代RTTI。
疑惑: 啥叫双重分配方案 啥叫访问者模式。

5.4.  const 的使用
原文:Use const whenever it makes sense. With C++11, constexpr is a better choice for some uses of const.
缺点:const是入侵的, 一个const对象做传入函数传入参数要求函数形参声明本身也是const的。 在调用库函数时常常会遇到问题。
要点:const修饰变量、成员、方法和类使得问题能在编译期就被发现, 问题当然越早暴露越好, 所以我们强烈推荐只要能用const,就用const,尤其在以下场景中:
1. 如果函数不会修改传入的引用或指针类型参数, 该参数应声明为 const.
2. 尽可能将函数声明为 const. 访问函数应该总是 const. 其他不会修改任何数据成员, 未调用非 const 函数, 不会返回数据成员非 const 指针或引用的函数也应该声明成 const.
3. 如果数据成员在对象构造之后不再发生变化, 可将其定义为 const.
扩展:
1. 要点场景三种的对象构造不是指构造函数函数体中, 而是指构造函数的初始化列表中,因为初始化列表才是第一次构造成员变量,在函数体重初始化const成员会编译错误。
class consttest{public:    consttest(int a):a_(a){} //right    consttest(int a){a_=a} //compile error    const int a_;};
2. 提到const的使用, 需要联想到mutable 和volatile的使用。
3. c++11中constexpr 的使用。

5.4.  constexpr 的使用
留个坑, 对于这个新特性不太了解。


5.5 六十四位下的可移植
原文:Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.
要点: 
1. 格式化输出
对于某些类型, printf() 的指示符在 32 位和 64 位系统上可移植性不是很好. C99 标准定义了一些可移植的格式化指示符. 不幸的是, MSVC 7.1 并非全部支持, 而且标准中也有所遗漏, 所以有时我们不得不自己定义一个丑陋的版本 (头文件 inttypes.h 仿标准风格):(具体见原文)
2.  谨记 sizeof(void*) !=  sizeof(int)
3.  定义64位常量时加上ULL或LL是个好习惯。 如:
int64_t my_value = 0×123456789LL;uint64_t my_mask = 3ULL << 48;
4.  如果你的代码一定要区分32位和64位系统,非这样不可得话, 建议用#ifdef _LP64来区分
疑惑:如果按照google所要求的格式化输出,看上去很麻烦的样子。 

5.6 预定义宏
原文:Be very cautious with macros. Prefer inline functions, enums, and const variables to macros.
要点:
1. 如果能不用预定义宏就不用预定义宏(除了头文件#findef..是个例外, google推荐所有头文件都用宏保护)。 最主要的原因是宏的引入使得你看到的代码和编译器看到的代码不一致,这会带来很多潜在的问题。
2. 有一部分宏定义可以被内联函数代替, 有一部分宏可以被const常量代替, 还有一部分宏(用来简短变量名)可以被c++引用代替。
3. 宏的#以及##功能,宏的区分代码功能不能被c+里别的特性代替,但在使用前考虑一下是否是非用不可?
建议:
1. 不要在.h中定义宏(一种情况例外)
2. 在使用前#define 在使用后#undef
3. 对于已存在(在别处定义的宏),不要为了自己使用它而把它undef后重新define。 尽量使用一个新的宏变量。
4. Try not to use macros that expand to unbalanced C++ constructs, or at least document that behavior well.
5. 尽量不要在宏里使用连接符“##” 来产生变量名,函数名和类名。
疑惑:
1. 建议的第一点, 好像在我们代码里很多宏都定义在头文件中。 
2. 建议的第四点, “ macros that expand to unbalanced C++ constructs,“是什么?
3. 建议的第五点, 搞清楚宏里面#和##的使用

5.7 auto的使用(c++11新特性)
原文:Use auto to avoid type names that are just clutter. Continue to use manifest type declarations when it helps readability, and never use auto for anything but local variables.
auto简介:
    在c++11中,关键字auto可以用来自动的关联的类型。你可以用关键字auto,通过copy或者引用赋值来初始化一个变量, 示例代码如下:
vector<string> v;...auto s1 = v[0];  // Makes a copy of v[0].const auto& s2 = v[0];  // s2 is a reference to v[0].
好处:
    当变量带上namespace或者和模板相关时, 其类型名会变得很长。使用auto能使代码更具可读性。示例如下:
//without autosparse_hash_map<string, int>::iterator iter = m.find(val);//with autoauto iter = m.find(val);
问题:
1. 有时候auto能带来刻度性, 有时候不能。比如auto a = x.find(y). 如果x和y的定义离得很远, 那么看这样的代码相当费神。
        2. 于此同时, auto还要求使用者和阅读者清楚auto和const auto&之间的区别, 二者区别关乎一次拷贝行为。
3. c++11中的auto和brace-initialization 一起使用时,会让人觉得非常迷惑。例如如下代码:
auto x(3);  // Note: parentheses.auto y{3};  // Note: curly braces.
 4. If an auto variable is used as part of an interface, e.g. as a constant in a header, then a programmer might change its type while only intending to change its value, leading to a more radical API change than intended.
建议:
允许使用auto,但只用于局部变量。不要将auto用在文件范围内或者namespace范围内的变量, 也不要跟将auto用作类的成员变量。Never assign a braced initializer list to an auto-typed variable.
    The auto keyword is also used in an unrelated C++11 feature: it's part of the syntax for a new kind of function declaration with a trailing return type. Function declarations with trailing return types are not permitted.
疑惑:
1. auto的使用
  2. brace-initialization 的使用

5.8 brace-initialization 
不了解,先挖坑。

5.9  lamdba expression
原文:Do not use lambda expressions, or the related std::function or std::bind utilities.
疑惑:不知道啥叫lamdbaexpression, 先挖坑

5.10 boost
定义:boost是各种peer-reviewed, 免费的开源的c++库的集合
优点:总体而言, boost库质量高,可移植性好,填补了许多c++标准库的空白--比如type traits, better binders, and better smart pointers.同时还提供了TR1(标准扩展库)的实现。
缺点:boost里面的很多库编程风格的可读性差, 比如metaprogramming and other advanced template techniques。 并且其编程风格过度的“functional
建议:为了代码的可读性和可维护性,我们只建议使用boost众多库中的如下部分(只是目前的列表, 将来可能会加入更多)
  • Call Traits from boost/call_traits.hpp
  • Compressed Pair from boost/compressed_pair.hpp
  • The Boost Graph Library (BGL) from boost/graph, except serialization (adj_list_serialize.hpp) and parallel/distributed algorithms and data structures (boost/graph/parallel/* and boost/graph/distributed/*).
  • Property Map from boost/property_map, except parallel/distributed property maps (boost/property_map/parallel/*).
  • The part of Iterator that deals with defining iterators: boost/iterator/iterator_adaptor.hppboost/iterator/iterator_facade.hpp, and boost/function_output_iterator.hpp
  • The part of Polygon that deals with Voronoi diagram construction and doesn't depend on the rest of Polygon: boost/polygon/voronoi_builder.hppboost/polygon/voronoi_diagram.hpp, and boost/polygon/voronoi_geometry_type.hpp
  • Bimap from boost/bimap 
  • Statistical Distributions and Functions from boost/math/distributions
还有下面几个库,虽然可以用, 但目前C++11中已经有替代品了。
  • Array from boost/array.hpp: use std::array instead.
  • Pointer Container from boost/ptr_container: use containers of std::unique_ptr instead.

5.11 C++11
原文:Use libraries and language extensions from C++11 (formerly known as C++0x) when appropriate. Consider portability to other environments before using C++11 features in your project.
定义:C++11是IOS 最新C++标准。他包含的很多改变对标准库和语言本身都有深远意义。
优点:C++11已经是官方标准,大部分的编译器最终都会支持C++11。它标准化了一些已经被业界使用的C++扩展, 它简化了一些操作,并对安全性和性能进行了优化。
缺点:
1. C++11总体上比他之前的C++标准复杂很多(标准手册1300页VS800页), 并且很多的程序员对他不太了解。它的一些特性对C++可读性和可维护性的长远影响目前还不能下定论。我们不知道我们所关心的工具何时能支持C++11的各种特性,在一个(因为某种原因)必须使用老版本工具的项目中, 这尤其是一个问题。
2. 就像之前谈到的boost库一样,C++11库体现的一些编程实践影响到了代码的可读性。Other extensions duplicate functionality available through existing mechanisms, which may lead to confusion and conversion costs.
建议:
如果不是一定需要C++11的一些特性,就不要用它。另外, 以下C++11中的特性最好别用(因为对他们不了解,原文拷贝如下)。
  • Functions with trailing return types, e.g. writing auto foo() -> int; instead of int foo();, because of a desire to preserve stylistic consistency with the many existing function declarations.
  • Compile-time rational numbers (<ratio>), because of concerns that it's tied to a more template-heavy interface style.
  • The <cfenv> and <fenv.h> headers, because many compilers do not support those features reliably.
  • Lambda expressions, or the related std::function or std::bind utilities.

命名规则
如果管理各种名字是代码一致性的最重要体现。 命名风格应该可以让我们从名字就能看出名字的实体是什么类型(一种类型?变量?常量?函数还宏)。
虽然命名规则看上去很武断(和多攻城狮都喜欢有自己风格的命名),但命名规则这件事上, 代码的一致性和可读性远比个人偏好重要。所以不管你喜不喜欢, 规则就是规则。
6.1 命名总体原则
函数名、变量名、文件名等都应该足够自描述, 避免使用简写。
名字越自描述越好。不要怕名字太长,命名能让一个新手也秒懂远比节省几个字节来的重要。不要使用那些语义模糊或者与项目上下文强相关的缩写语, 更不要通过删除单词里面的几个字母来缩写。
正确范例:
int price_count_reader;    // No abbreviation.int num_errors;            // "num" is a widespread convention.int num_dns_connections;   // Most people know what "DNS" stands for.
错误范例:
int n;                     // Meaningless.int nerr;                  // Ambiguous abbreviation.int n_comp_conns;          // Ambiguous abbreviation.int wgc_connections;       // Only your group knows what this stands for.int pc_reader;             // Lots of things can be abbreviated "pc".int cstmr_id;              // Deletes internal letters.

6.2 文件名 
1. 文件名应该只使用小写单词,允许使用下划线(_)或者破折号(-)做连接符(具体使用哪种需要和工程保持一致)。 如果没有工程一致性的要求, 我们建议使用下划线。以下是文件命名的正确示范。
my_useful_class.ccmy-useful-class.ccmyusefulclass.ccmyusefulclass_test.cc // _unittest and _regtest are deprecated.
2.  C++ 文件应该以.cc结尾。 头文件以.h结尾。
3. 不要使用已经存在于/urs/include 下的文件名。 比如 db.h
4. 总体而言,让你的文件名字意义明确。比如, 用http_server_logs.h做文件名就比用logs.h做文件名好。一种常见的场景是命名一对文件, 比如 foo_bar.h和foo_bar.cc共同定义了类FooBar。
5. 内联函数必须定义在.h文件中。如果内联函数代码量小, 可以直接放在.h里。如果代码量大建议将所有内联函数都集中放到一个以-inl.h结尾的文件中。 如果一个类中有很多内联函数, 那么这个类将会用三个文件来定义。例如:
url_table.h      // The class declaration.url_table.cc     // The class definition.url_table-inl.h  // Inline functions that include lots of code.
6.2 类型名
1. 类型名(包括类名,结构体名,枚举类型名,typedef出来的类型名等)都以大写字母开头,不适用下划线, 每一个单词的开头字母大写。例如:
// classes and structsclass UrlTable { ...class UrlTableTester { ...struct UrlTableProperties { ...// typedefstypedef hash_map<UrlTableProperties *, string> PropertiesMap;// enumsenum UrlTableErrors { ...

6.3 变量名
总的来说, 变量名都使用小写,可以使用下划线连接单词(可以不使用)。内成员变量名后缀下划线。 示例:my_exciting_local_variable, my_exciting_member_variable_。
1. 普通变量名
string table_name;  // OK - uses underscore.string tablename;   // OK - all lowercase.string tableName;   // Bad - mixed case.
2. 类成员变量名, 除了结尾加下划线, 其他和平台变量名一致。 示例:
string table_name_;  // OK - underscore at end.string tablename_;   // OK.
3. 结构体成员变量, 命名规则和普通变量一致。 示例:
struct UrlTableProperties {  string name;  int num_entries;}
4. 全局变量。 尽量少用全局变量, 如果需要使用,建议变量名以g_开头, 以和普通变量区分开来。

6.4  常量命名
以k开头,后面的名字使用大小写结合的方式(类似于类型名,每一个单词第一个字母大写)。比如 kDaysInWeek
所有编译期常量,无论是局部定义的, 全局定义的或者是定义在类里面的, 都使用以k开始后面接大小写结合的命名方式。示例:
const int kDaysInAWeek = 7;

6.5 函数名
函数命名分两种情况:
1. 普通的函数
普通函数命名才有大小写结合的方式(每一个单词第一个字母大写),不使用下划线。如果你的函数可能引起崩溃(特指那种会被产品代码调用,并且即使调用正常,也可能崩溃的函数),那么添加一个OrDie到你的函数名。 示例:
AddTableEntry()DeleteUrl()OpenFileOrDie()
2. Accessors和Mutators函数(即set和get函数)应该和对于的变量名保持一致, 例如:
class MyClass { public:  ...  int num_entries() const { return num_entries_; }  void set_num_entries(int num_entries) { num_entries_ = num_entries; } private:  int num_entries_;};
3. 对已极短的内联函数, 也可以使用全小写的命名规则(意味着这样的函数调用性能损耗很低,和使用一个cache住的变量一样)。

6.6 namespace 命名
 namespace命名采用全小写方式, 可用下划线。为避免冲突,namespace命名建议是基于项目名+路径结构命名。 比如:project_path_something

6.7 宏命名
  如果你非使用宏的话(我们不建议使用宏), 宏的命名采用全大写+下划线命名。 示例:
#define ROUND(x) ...#define PI_ROUNDED 3.0

6.8 命名规则的例外
可读性和一致性是命名规则的目的。 所以如果你定义的是一个同C或C++库中某个函数功能类似的函数, 那么你应该按照库函数名的命名方式命名。  示例:
bigopen()function name, follows form of open()uinttypedefbigposstruct or class, follows form of possparse_hash_mapSTL-like entity; follows STL naming conventionsLONGLONG_MAXa constant, as in INT_MAX

7 注释
注释里有几点要注意。 
1. 多写注释。  
2. c++注释可以选择//或者/**/, 但要保持一致。 
3. 注释少用短语, 尽量语义完整。
4. TODO后面总是加上责任人如//TODO(fridazhu@gmail.com)

8. 代码格式
8.1.  一行代码字符数不要超过80个
8.2. 字符集
1. 尽量没有非ASCII字符。如果一定要有,使用UTF-8字符集。
2. 尽量不要在代码里硬编码面向用户的文档。 如果一定要,二期文档中有非ASCII字符, 使用UTF-8
3. 可以使用十六进制编码
疑惑: 十六进制编码这一节没太理解,平时也很少用过。 
8.3  空格和Tab
不适用Tab, 只使用空格。两个空格一个缩径。应该让你的编辑器自动将Tab替换为固定个数的空格。

8.4 函数声明与定义
返回类型和函数名在同一行, 参数也尽量放在同一行.函数看上去像这样:
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {    DoSomething();    ...}
如果同一行文本太多, 放不下所有参数:
ReturnType ClassName::ReallyLongFunctionName(Type par_name1,                                             Type par_name2,                                             Type par_name3) {    DoSomething();    ...}
甚至连第一个参数都放不下:
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(        Type par_name1,  // 4 space indent        Type par_name2,        Type par_name3) {    DoSomething();  // 2 space indent    ...}
注意以下几点:
  • 返回值总是和函数名在同一行;
  • 左圆括号总是和函数名在同一行;
  • 函数名和左圆括号间没有空格;
  • 圆括号与参数间没有空格;
  • 左大括号总在最后一个参数同一行的末尾处;
  • 右大括号总是单独位于函数最后一行;
  • 右圆括号和左大括号间总是有一个空格;
  • 函数声明和实现处的所有形参名称必须保持一致;
  • 所有形参应尽可能对齐;
  • 缺省缩进为 2 个空格;
  • 换行后的参数保持 4 个空格的缩进;
  •   

    如果函数声明成 const, 关键字 const 应与最后一个参数位于同一行
    // Everything in this function signature fits on a single lineReturnType FunctionName(Type par) const {  ...}// This function signature requires multiple lines, but// the const keyword is on the line with the last parameter.ReturnType ReallyLongFunctionName(Type par1,                                  Type par2) const {  ...}<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);"></span>

    如果
    参数
    没有用到, 可以将参数名注释起来。
      
    // Always have named parameters in interfaces.class Shape { public:  virtual void Rotate(double radians) = 0;}// Always have named parameters in the declaration.class Circle : public Shape { public:  virtual void Rotate(double radians);}// Comment out unused named parameters in definitions.void Circle::Rotate(double /*radians*/) {}
    // Bad - if someone wants to implement later, it's not clear what the// variable means.void Circle::Rotate(double) {}

    8.5 函数的调用
        尽量一行搞定, 一行搞不定则按照做括号起始处将参数分成多行。一下都是正确范例:
    bool retval = DoSomething(argument1, argument2, argument3);
    bool retval = DoSomething(averyveryveryverylongargument1,                          argument2, argument3);
    bool retval = DoSomething(argument1,                          argument2,                          argument3,                          argument4);if (...) {  ...  ...  if (...) {    DoSomething(        argument1,  // 4 space indent        argument2,        argument3,        argument4);  }


    8.1x Braced Initializer Lists
    留个坑。

    8.1x 类定义
    1. 如果不超过80列, 类定义时基类和子类应该在同一行。(即继承关系在一行内)
    2. public、protected、private关键字前面有一个空格的缩进
    3. 除了第一个关键字(一般是public:),其他关键字前应该空一行。如果类很小的话可以不空。
    4. 不要在这些关键字后面空行
    5. 关键字顺序应该是 public  protected 最后private

    8.1x namespace定义
    1. namespace 不引入额外的缩进
    namespace {void foo() {  // Correct.  No extra indentation within namespace.  ...}}  // namespace
    namespace {  // Wrong.  Indented when it should not be.  void foo() {    ...  }}  // namespace
    2. 当声明嵌套的namespace时, 每一个namespace占一行
    namespace foo {namespace bar {


    其他:
    windows C++程序员容易使用的一些编程习惯,应该避免这些
    1.  变量的匈牙利命名法, 如定义一个整数为 int iNum;   应该按照google 的风格命名。
    2.  使用#progma once。 google建议任何头文件都显式手动的用宏来保护。
    3.   实现文件命名为.cpp; 建议命名为.cc
    4.   the usual way of working with precompiled headers is to include a header file at the top of each source file, typically with a name like StdAfx.h or precompile.h. To make your code easier to share with other projects, avoid including this file explicitly (except in precompile.cc), and use the /FI compiler option to include the file automatically.






  • 0 0