试着看了一下Google Style的C++编码规范,先随手写了一个最简单的程序来试试Cpplit。代码如下
————————————
#include <iostream>
using namespace std;
int main() {
cout << “Hello World!” << endl;
return 0;
}
————————————
运行指令
————————————
g++ main.cpp -o main
./main
————————————
得到结果
————————————
Hello World!
————————————
运行Cpplint,得到的结果如下
————————————
main.cpp:0: No copyright message found. You should have a line: “Copyright [year] <Copyright Owner>” [legal/copyright] [5]
main.cpp:1: Streams are highly discouraged. [readability/streams] [3]
main.cpp:3: Do not use namespace using-directives. Use using-declarations instead. [build/namespaces] [5]
Done processing main.cpp
Total errors found: 3
————————————
第一条说我没有版权信息,第二条没看懂,第三条说的是不能用std全部空间,建议使用具体用到的函数。
仔细上网查了半天,终于弄懂了第二条是什么意思。
——————————————
5.9. 流
Tip 只在记录日志时使用流.
定义: 流用来替代 printf() 和 scanf().
优点: 有了流, 在打印时不需要关心对象的类型. 不用担心格式化字符串与参数列表不匹配 (虽然在 gcc 中使用 printf 也不存在这个问题). 流的构造和析构函数会自动打开和关闭对应的文件.
缺点: 流使得 pread() 等功能函数很难执行. 如果不使用 printf 风格的格式化字符串, 某些格式化操作 (尤其是常用的格式字符串 %.*s) 用流处理性能是很低的. 流不支持字符串操作符重新排序 (%1s), 而这一点对于软件国际化很有用.
结论: 不要使用流, 除非是日志接口需要. 使用 printf 之类的代替.
使用流还有很多利弊, 但代码一致性胜过一切. 不要在代码中使用流.
拓展讨论:
对 这一条规则存在一些争论, 这儿给出点深层次原因. 回想一下唯一性原则 (Only One Way): 我们希望在任何时候都只使用一种确定的 I/O 类型, 使代码在所有 I/O 处都保持一致. 因此, 我们不希望用户来决定是使用流还是 printf + read/write. 相反, 我们应该决定到底用哪一种方式. 把日志作为特例是因为日志是一个非常独特的应用, 还有一些是历史原因.
流的支持者们主张流是不二之选, 但观点并不是那么清晰有力. 他们指出的流的每个优势也都是其劣势. 流最大的优势是在输出时不需要关心打印对象的类型. 这是一个亮点. 同时, 也是一个不足: 你很容易用错类型, 而编译器不会报警. 使用流时容易造成的这类错误:
cout << this; // Prints the address
cout << *this; // Prints the contents
由于 << 被重载, 编译器不会报错. 就因为这一点我们反对使用操作符重载.
有人说 printf 的格式化丑陋不堪, 易读性差, 但流也好不到哪儿去. 看看下面两段代码吧, 实现相同的功能, 哪个更清晰?
cerr << “Error connecting to ‘” << foo->bar()->hostname.first
<< “:” << foo->bar()->hostname.second << “: ” << strerror(errno);
fprintf(stderr, “Error connecting to ‘%s:%u: %s”,
foo->bar()->hostname.first, foo->bar()->hostname.second,
strerror(errno));
你可能会说, “把流封装一下就会比较好了”, 这儿可以, 其他地方呢? 而且不要忘了, 我们的目标是使语言更紧凑, 而不是添加一些别人需要学习的新装备.
每一种方式都是各有利弊, “没有最好, 只有更适合”. 简单性原则告诫我们必须从中选择其一, 最后大多数决定采用 printf + read/write.
——————————————
最终改好的程序如下,Cpplint能通过,g++也能通过
——————————————
// Copyright 2011 Bill_Lang
#include <cstdio>
int main() {
printf(“Hello World!”);
return 0;
}
——————————————
直接看Google Style会一会儿就烦了,但是配合着使用Cpplint,还是挺有乐趣的。看来还能帮助自己提高C++编程的实际水平。
推荐朋友们也玩玩这个。
from:http://billlangjun.wordpress.com/2011/03/31/
========================================================================
写代码时必须注重代码规范。养成良好的程序风格有助于提高代码的可维护性与可阅读性。
Google推出了自己的《Google C++ 风格指南》,在这份指南中各种C++编程中遇到的风格问题都得到了规范。由于原版篇幅过长,我在这里整理一份简化版的编程规范,以供各位参考。同时希望每一位狂想曲创作组成员都能够用此代码规范编程。
注意:此份资料仅仅是Google推荐的代码风格,不代表所有程序都要用此套规范编写,当然我们还是强烈建议在建立任何项目或是工程之前,都应该统一风格,以便日后维护。
此套代码规范与常用的一些规范(如匈牙利命名法等)可能有所出入,如果您是第一次使用,也许会感到些许不适应,但还请多多习惯此种编码规范。
另:本文档仅仅介绍的是C++编码风格指南,而并非编程规范或是编程优化指南,如若想了解更多的代码优化方法(如使用何种输入输出方式等)还请参考《effective C++》与《more effective C++》等著作。
好了,下面开始我们正式的风格规范探索。
一、头文件
1. 头文件写在.h文件中,实现文件写在.cc中而不是写在.cpp中。
2. 每个.cc文件都对应一个.h文件,包含main()函数的文件除外。
3. 每个头文件都要用#define保护,格式:
#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ … #endif // FOO_BAR_BAZ_H_
4. 只有当函数少于10行时才建议使用内联函数,内联中包含循环或switch语句反而会降低效率。(Frankie注:不建议在内联函数中再调用其他函数)
5. 复杂的内联函数与函数模板建议放在-inl.h文件中,并用#define保护。
6. 函数参数顺序:先输入参数,然后输出参数。
7. #include路径及顺序:该.cc的.h头文件、C库、C++库、其他库的.h文件、本项目内的.h文件。
二、作用域
1. 鼓励在.cc文件内使用匿名名字空间。不要在.h文件中使用匿名空间。
2. 使用具名的名字空间时用名字把.h文件及.cc文件中除前置声明外的源文件全部封装起来。
// .h 文件 namespace mynamespace { // 所有声明都置于命名空间中 // 注意不要使用缩进 class MyClass { public: … void Foo(); }; } // namespace mynamespace // .cc 文件 namespace mynamespace { // 函数定义都置于命名空间中 void MyClass::Foo() { … } } // namespace mynamespace
3. 最好不使用using namespace,而使用using ::foo::bar;类型。
4. 尽量不使用裸的全局函数,而要使用静态成员函数或名字空间内的非成员函数。
5. 尽可能将变量至于最小作用域内,尽可能用初始化方式替代声明再赋值,遇到for循环则要考虑效率问题(@July,代码大全,深入理解计算机系统都有阐述)。如:
// 低效的实现 for (int i = 0; i < 1000000; ++i) { Foo f; // 构造函数和析构函数分别调用 1000000 次! f.DoSomething(i); }
// 高效的实现 Foo f; // 构造函数和析构函数只调用 1 次 for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
6. 禁止使用class类型的静态或全局变量,包括STL容器,尤其是在多线程中。
三、类
1. 构造函数只初始化那些没什么意义的(trivial)数据,有意义的(non-trivial)数据建议使用Init()函数。
2. 如果类定义了若干成员变量且无其他构造函数,则需要定义一个默认构造函数。
3. 仅当只有数据时使用struct,其他一概使用class。
四、其他C++特性
1. 输入参数是值参或const引用,输出参数为指针。输入参数可以为const指针,但决不能为非const的引用参数。
2. 仅在输入参数类型不同但功能相同时使用重载函数,不要用函数重载去模拟缺省函数参数。
3. 不允许使用缺省函数参数。
4. 不使用异常。(Frankie注:我的理解是这是因为Google自身代码的健壮性造成的。Google认为使用异常并不能提高效率)
5. 禁止使用RTTI。
6. 使用C++类型转换,如static_cast<>()而不是不要使用 int y = (int)x或 int y = int(x) 等转换方式。
7. 不要使用流而要使用printf()与scanf()(@July,尽量,但不盲从)。记录日志除外。
8. 尽量使用前置自增自减,尤其是对迭代器和模板类型。
9. 尽可能使用const。
10. 使用断言来指出变量为非负数,而不是使用无符号整型。
11. 整数用 0, 实数用 0.0, 指针用 NULL, 字符 (串) 用 '\0'。
12. 尽可能用 sizeof(varname) 代替 sizeof(type)。
13. 只是用Boost中被认可的库。
五、命名规定
1. 尽可能给出描述性的名称,如:int num_errors; int num_completed_connections;
2. 函数名通常是指令性的,如OpenFile(), set_num_errors()。
3. 文件名要全部小写,可以包含下划线或连字符。(Frankie注:《狂想曲》统一使用下划线)
4. 类型名称的每个单词首字母均大写且不含下划线:MyExcitingClass, MyExcitingEnum。
5. 变量命名一律小写,单词之间以下划线连接。类成员变量以下划线结尾。如:
my_exciting_local_variable
my_exciting_member_variable_
6. 结构体变量的数据成员命名与普通变量一样。
7. 常量命名在名称前加k:kDaysInAWeek。
8. 常规函数每个单词首字母大写,没有下划线。
9. get和set函数要与存取的变量名匹配。如:
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_; };
10. 名字空间用小写字母命名,用下划线连接单词。
11. 枚举的命名与常量或宏一致:kEnumName或ENUM_NAME,并且建议使用前者,即常量风格的命名方式。
12. 宏命名全部大写,单词之间用下划线连接。(Frankie注:#define保护文件要用下划线结尾)
六、注释(Frankie注:此处为本人根据实际结合Google代码规范提出的注释方法,适用于《狂想曲》)
1. 使用。
2. 在每个文件头加入版权公告,注明作者、修改日期、版本号等信息(只要是我们内部人员,就只标明作者)。
3. 函数或者变量等的注释没有太多要求,只需注意对齐,内容详尽即可。
4. 对尚未完成或者需要改进的代码使用TODO注释。如:
// TODO(kl@gmail.com) Use a "*" here for concatenation operator. // TODO(Zeke) change this to use relations.
TODO需要大写,括号内是您的名字,后接修改的方案等,是为了在“将来某一天做某事”,可以加上明确的时间或明确的事项。
七、格式(Frankie注:格式内容涉及面较广,详细请参见Google代码风格指南源文档)
1. 每行代码字符数不超过80。
2. 用UTF-8编码。
3. 只是用空格不适用制表位,每次缩进2个空格。
4. 返回类型和函数名在同一行,参数尽量放在同一行。
5. if, else, switch等语句一定要使用大括号{}。