yolocai 2017.8.3
`void Foo(const string &in, string *out);`
如果您打算重载一个函数, 可以试试改在函数名里加上参数信息。例如,用 AppendString() 和 AppendInt() 等, 而不是一口气重载多个 Append().
值得庆幸的是, C++ 中, 宏不像在 C 中那么必不可少. 以往用宏展开性能关键的代码, 现在可以用内联函数替代. 用宏表示常量可被 const 变量代替. 用宏 “缩写” 长变量名可被引用代替. 用宏进行条件编译… 这个, 千万别这么做, 会令测试更加痛苦 (#define 防止头文件重包含当然是个特例).
下面给出的用法模式可以避免使用宏带来的问题; 如果你要宏, 尽可能遵守:
+ 不要在 .h 文件中定义宏.
+ 在马上要使用时才进行 #define, 使用后要立即 #undef.
+ 不要只是对已经存在的宏使用#undef,选择一个不会冲突的名称;
+ 不要试图使用展开后会导致 C++ 构造不稳定的宏, 不然也至少要附上文档说明其行为.
+ 不要用 ## 处理函数,类和变量的名字。
```
// Vector 接收了一个初始化列表。
vector v{"foo", "bar"};
// 不考虑细节上的微妙差别,大致上相同。
// 您可以任选其一。
vector v = {"foo", "bar"};
// 可以配合 new 一起用。
auto p = new vector{"foo", "bar"};
// map 接收了一些 pair, 列表初始化大显神威。
map m = {{1, "one"}, {2, "2"}};
// 初始化列表也可以用在返回类型上的隐式转换。
vector test_function() { return {1, 2, 3}; }
// 初始化列表可迭代。
for (int i : {-1, -2, -3}) {}
// 在函数调用里用列表初始化。
void TestFunction2(vector v) {}
TestFunction2({1, 2, 3});
```
#include
的路径及顺序使用标准的头文件包含顺序可增强可读性, 避免隐藏依赖: 相关头文件, C 库, C++ 库, 其他库的 .h, 本项目内的 .h.
int i;
i = f(); // 坏——初始化和声明分离
int j = g(); // 好——初始化时声明
vector<int> v;
v.push_back(1); // 用花括号初始化更好
v.push_back(2);
vector<int> v = {1, 2}; // 好——v 一开始就初始化
如果变量是一个对象, 每次进入作用域都要调用其构造函数, 每次退出作用域都要调用其析构函数.
// 低效的实现
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);
}
不要定义隐式类型转换. 对于转换运算符和单参数构造函数, 请使用 explicit 关键字.
class String{
public:
explicit String(int n){};
String(const char *p){};
};
void Func(Foo f){};
int main(){
//String s1 = 'a'; //错误:不能做隐式char->String转换
String s2(10); //可以:调用explicit String(int n);
String s3 = String(10);//可以:调用explicit String(int n);再调用默认的复制构造函数
String s4 = "Brian"; //可以:隐式转换调用String(const char *p);再调用默认的复制构造函数
String s5("Fawlty"); //可以:正常调用String(const char *p);
}
仅当只有数据成员时使用 struct, 其它一概使用 class.
所有继承必须是 public 的. 如果你想使用私有继承, 你应该替换成把基类的实例作为成员对象的方式.
不要过度使用实现继承. 组合常常更合适一些. 尽量做到只在 “是一个” (“is-a”, 其他 “has-a” 情况下请使用组合) 的情况下使用继承: 如果 Bar 的确 “是一种” Foo, Bar 才能继承 Foo.
必要的话, 析构函数声明为 virtual. 如果你的类有虚函数, 则析构函数也应该为虚函数.
对于可能被子类访问的成员函数, 不要过度使用 protected 关键字. 注意, 数据成员都必须是 私有的.
对于重载的虚函数或虚析构函数, 使用 override, 或 (较不常用的) final 关键字显式地进行标记. 较早 (早于 C++11) 的代码可能会使用 virtual 关键字作为不得已的选项. 因此, 在声明重载时, 请使用 override, final 或 virtual 的其中之一进行标记. 标记为 override 或 final 的析构函数如果不是对基类虚函数的重载的话, 编译会报错, 这有助于捕获常见的错误. 这些标记起到了文档的作用, 因为如果省略这些关键字, 代码阅读者不得不检查所有父类, 以判断该函数是否是虚函数.
类定义一般应以 public: 开始, 后跟 protected:, 最后是 private:. 省略空部分.
函数的参数顺序为: 输入参数在先, 后跟输出参数.
int price_count_reader; // 无缩写
int num_errors; // "num" 是一个常见的写法
int num_dns_connections; // 人人都知道 "DNS" 是什么
int n; // 毫无意义.
int nerr; // 含糊不清的缩写.
int n_comp_conns; // 含糊不清的缩写.
int wgc_connections; // 只有贵团队知道是什么意思.
int pc_reader; // "pc" 有太多可能的解释了.
int cstmr_id; // 删减了若干字母.
文件名要全部小写, 可以包含下划线 () 或连字符 (-), 依照项目的约定. 如果没有约定, 那么 “” 更好.
类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum.
变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.
常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: MyExcitingFunction(),
MyExcitingMethod(),
my_exciting_member_variable(), set_my_exciting_member_variable().