《Google C++编码规范》读书笔记第五章:其它C++特性

《Google C++编码规范》读书笔记第五章:其它C++特性

一.引用参数(Reference Arguments)

定义:如果需要改变参数的值,在C语言中可以使用指针形参,在C++中还可以使用引用形参
优点:使用引用形参更优雅一点,例如拷贝构造函数,而且不必像指针那样接受空指针NULL。
缺点:容易引起误解,引用在语法上是拥有指针的语意的。
总结:在函数形参列表中,如果有引用形参,必须是const
硬性约定:输入参数为值或者常量引用时,输出参数为指针;输入参数可以是常量指针,但不能使用非常量引用形参。

二.函数重载(Function Overloading)

定义:输入参数不同、功能相同时使用函数重载(含构造函数),不要使用函数重载模仿缺省函数参数。
可以定义一个函数形参为const string&,并定义其重载函数为const char*,例如:

class MyClass {
public:
    void Analyse(const string& text);
    void Analyse(const char *text, size_t len);
};

优点:不同参数的同名函数,代码直观,访问者便利,一个接口,多种实现。
缺点:函数调用即寻找最佳的函数匹配,有时候很难确定调用的时那个重载函数;另外一个原因是当派生类只重载函数部分会令跟多人对继承语意产生困惑。
结论:如果像重载函数,考虑让函数名包含参数信息

三.缺省参数(Default Arguments)

禁止使用缺省函数参数
优点:一个函数带有大量的常用缺省值,使用起来比较方便。
缺点:查看API使用函数时,缺省参数使得代码难以呈现所有参数,当缺省参数不适用于新代码时可能导致重大问题。
结论:所以参数必须明确指定,避免使用可能不为人知的缺省参数。

四.变长数组和alloc(Variable-Length Arrays and alloca())

禁止使用边长数组和alloc()。

//变长数组
struct data{
    int len;
    int data_mem[0];
};

缺点:都不是C++标准组成部分,更重要地是,它们在堆栈上根据数据分配大小可能导致难以发现的内存泄露错误。
结论:使用安全的分配器(allocator),如:scoped_ptr/scoped_array。

五.友元(Friends)

允许合理使用友元类以及友元函数。
通常友元定义再统一文件下,避免其他文件中查找对某个类私有成员的使用。某些情况下,将一个单元测试类声明为待测类的友元会很方便。
友元延伸了类的封装界限,只允许另一个类访问某个成员时,使用友元通常比将其声明为public好的多。

六.异常(Exception)

不要使用C++异常。(这是google的原则)

优点:
1. 异常允许上层应用决定如何处理在底层嵌套函数中发生的“不可能发生”的失败,不像出错代码的记录那样模糊费解。
2. 应用在很多现代语言中,引入异常使得C++与Python,java等更加兼容。
3. 异常是解决构造函数失败的唯一方法,虽然可以通过工厂函数或者Init()模拟异常,但是需要堆分配。
4. 许多C++第三方库使用异常,关闭异常难以与之结合。
5. 在测试框架中,异常很好用。

缺点:
1. 在现有函数中添加throw语句时,必须检查所有调用处,必须具有基本的异常安全保护,或者程序正常结束,永远不可能捕获该异常。
2. 异常会导致程序控制流,通过查看代码无法确认:函数有时候可能在不确定的地方返回,从而导致代码管理和调试困难。
3. 异常安全需要RAII和不同的编码实践。轻松、正确地编写异常安全的代码需要大量支持。
4. 加入异常使二进制执行代码体积变大,可能增加了编译时长,还可能增加地址空间压力。

总结:由于Google的现有C++代码都没有异常处理,因此引入带有异常处理的新代码相当困难。

七.运行时类型识别(RTTI)

禁止使用RTTI。
定义:RTTI允许程序员在运行期识别C++对象的类型。
优点:RTTI在某些单元测试中很有用。
缺点:运行时识别类型通常说明设计本身有问题。

八.类型转换(Casting)

使用C++的类型转换符。
优点:更容易,更醒目,能够帮助编译器诊断错误。
缺点:语法相对复杂。
1. static_cast: 和C风格相似的可做值的强制转换,或者指针的父类到子类的向上转换。
2. const_cast: 移除const属性。
3. reinterpret_cast: 指针类型和整型或者其它指针的不安全的转换,常用于函数指针的转换。
4. dynamic_cast: 类继承体系中自上而下的安全转型,处单元测试框架外,不要使用。

九.流(Streams)

只有在记录日志时使用流。
定义:流是printf()和scanf()的替代。
优点:有了流,在输出时不必担心对象的类型,不必担心格式化字符串于参数不匹配,打开关闭对应文件时,流可以自动构造、析构。
缺点:流使得pread()等功能函数很难执行,光使用流很难对格式进行操作。

十.前置自增和自减(Preincrement and Predecrement)

对于迭代器和其它模板对象使用前置的递增、递减形式。
优点:前置递增、递减通常比后置的效率高,因为后置返回的是对象的拷贝,以右值形式返回,如果对象是迭代器或者非数值类型,拷贝的代价很大,所以使用前置更好。
缺点:C语言使用的是传统的后置,特别是在for循环中,更加易懂。

十一.const的使用(use of const)

强烈建议使用const.
定义:const表示只读。
优点:更容易理解变量是如何使用,编译器可以更好地进行类型检查、更好地生成代码。
缺点:如果向一个函数传入const变量,函数原型中也必然是const,在调用库函数时比较麻烦,需要使用const_cast类型转换。

十二.整型(Integer Types)

C++内建整型中,唯一用到的就是int,如果程序需要大小不同的变量,可以使用<stdint.h>中的精确宽度的整型。例如:

int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t

需要确定大小的整型时,可以使用它们代替short, int, long等。在C整型中,只使用int,适当情况下使用便准类型例如:size_tptrdiff_t
使用断言声明变量为非负数,不要使用无符号型。

十三.64位下的可移植性(64-bits Protability)

  1. 对于结构体对齐尤为小心,在64位系统中,默认以8字节对齐,可以使用
    #pragma pack()
  2. 创建64位常量时使用LL或者ULL作为后缀,例如:
int64_t my_value = 0x123456789LL;
uint_64_t my_mask = 3ULL << 48;
  1. 如果32位系统和64系统需要不同的代码,提前说明之。

十四.预处理宏(Preprocessor Macros)

使用宏时必须小心,最好使用内联函数,枚举和常量代替之。
宏意味着你和编译器看到的代码不一致,因此可能导致异常行为,尤其是当宏处于全局作用域中时。
在C++中,宏内联可以使用内联函数代替;宏常量可以使用const变量代替。
参考:
1. 不要在头文件中定义宏;
2. 使用前正确#define,使用后记得#undef;
3. 不要只是对一个已经存在的宏使用#undef,选择一个不会冲突的名称;
4. 不使用会导致不稳定的C++构造的宏,至少说明其行为。

十五.0和NULL

表示空的:整数用0,实数用0.0,指针用NULL(在C++11中,最好使用nullptr),字符串用’\0’。

十六.sizeof

尽可能使用sizeof(varname)代替sizeof(type)

十七.Boost库

只使用Boost中被认可的库。
定义:Boost库是一个受欢迎的、同行评议(peer-reviewed)、免费的、开源的C++库。
优点:Boost库代码质量高,可移植性好,填补了C++标准库的许多空白,如:性别特性(type traits)、更加完善的绑定(binders)、更好的智能指针,同时还提供了TR1(标准库的扩展)的实现。
缺点:某些编程实践可读性差,像元编程(metaprogramming)和其它的高级模板技术,以及过度“函数化”(functional)的编程风格。
结论:使用认可的库,包括:
1. boost/compressed_pair.hpp;
2. boost/ptr_container

主要参考自《Google C++编码规范》中文版

猜你喜欢

转载自blog.csdn.net/qq_25467397/article/details/80956773