C++编码规范系列 开篇
开篇
C++是一种在设计之初,便兼顾性能和设计,拥有罕见的威力和丰富的表达能力,只要运用得当,可以表现出巨大而变化多端的设计和优秀的性能体现,然而没有良好的规范,可能导致代码难以理解,难以维护,可扩展性低而又性能拙略。所以系统的在工作和学习中进行归纳总结是非常必要的。
此系列文章是参考书本的基础上,旨在结合自己的理解对C++和软件架构进行系统的学习笔记。从软件系统和操作系统层面,兼顾设计和性能去系统的学习。
编码规范-新特性-并发编程-设计模式-代码重构-架构
内核基础(进程调度,通信,内存管理…)
C++编码规范
1. prefer consts,enums and inlines to #define.
宏定义不被视为语言的一部分,在编译器开始处理源码之前就被预处理器替换掉了,不利于进行代码调试,即使用const enum替换掉#define,使用inline替换掉函数宏。
为了被不同的源码包含,常量定义式通常放在头文件中:因此有必要将指针(而不是所指之物)声明为const,
例如:
const char* const authorName = "scott Meyers";
注:string对象比char* (Base)合适;
为了常量作用域限定在class内,则需要将其设置为一个成员,而为了保证常量的唯一性,需要将其设定为static成员;在声明式中获得初值,只要不对它取地址,可以不提供定义式而使用它;
class demo
{
private:
static const double Const_PI = 3.1415926;
...
};
使用enum替代宏的特点是,对enum取地址不合法;
#include #ifdef/#ifndef控制编译的角色仍然是不可替代的。
2. use const whenever possible.
- const iterator等同于T* const,而const_iterator 等同于const T*;
- const成员函数不能对成员变量有任何修改,除非将成员变量声明为mutable:
class demo
{
public:
///返回text的长度,函数内部不可以对成员变量更改
std::size_t length() const;
private:
//除非声明为mutable
mutable std::size_t m_textLength;
static const double Const_PI = 3.1415926;
...
};
- 当const和no-const成员函数有着实质上等价的实现时,使用非const成员函数调用const版本的重载,可以解决代码膨胀的问题;而反过来,使用const成员函数调用non-const版本的成员函数则是错误的,因为const函数承诺不改变任何对象的属性而non-const版本则没有这样的承诺。
3. make sure that objects are initialized before they are used.
- 原则上确保每一个构造函数都将对象所有的成员初始化,但是赋值和初始化是不同的,C++规定对象的成员变量的初始化动作是发生在进入构造函数之前,但是这个规定无法保证基础类型在被赋值之前被初始化。高效的做法是在初始化列表中对所有成员进行初始化。
- 对于大多数类型,在构造函数中赋值,会首先调用default构造函数设定初值,再调用copy赋予新值;而在初始化列表中只执行一次参数的copy即可。对于基础类型,初始化和赋值成本相同,但是为了风格一致性,放在初始化列表中执行初始化比较适宜;对于const或者引用,必须使用初始化列表赋予初值。故,我们的做法是,总是使用初始化列表;
- class中的成员变量总是以其声明的顺序执行初始化,即使你在初始化列表中改变了顺序,也不会对初始化的次序有任何影响;
- base class早于derive class初始化;
- static对象指的是寿命从被构造出来一直到程序结束为止的对象。函数内部的static对象称为local static对象,否则称为non-static对象,C++保证,函数内的local static 对象会在该函数调用期间,首次遇上该对象的定义式时被初始化。------ 如果一个non-static对象初始化依赖于另一个non-static对象,但是它们初始化次序没办法保证,可以使用”函数调用“返回一个reference指向local static对象替换直接访问non-local static对象,保证获得的reference的那个指向一个初始化后的对象并且,如果没有调用这个函数,则避免了构造和析构的成本,bravo! - 单例模式
class demo
{
std::size_t func();
};
//返回一个引用指向local static对象
demo& do()
{
static demo _do;
return _do;
}
class demo1
{
...
std::size_t demo_size = do().func();
...
};
demo1& do()
{
static demo1 _do_1;
return _do_1;
}
4. What functions C++ silently writes and calls for us.
- 当你定义i了一个empty class时,c++处理后,编译器会帮你声明一个default 构造函数,析构函数,copy构造函数和一个copy assignment操作符,并且这些都是public且inline的。
- 如果一个内含reference或者const成员的class,必须自己定义copy assignment操作符,编译器无法决定怎么处理这些成员变量;如果某个base class将copy assignment操作符声明为private,编译器会拒绝为其derived classes生成copy assignment操作符,因为默认子类的copy assignment想象中是可以处理base的成分。
5. Explicitly disallow the use of compiler-generated functions you not want.
- 阻止编译器生成一个默认的成员函数,可以将其声明为private,阻止用户调用。如果在member函数或者friend函数内操作,则链接器会发出抱怨。
- 专门设计一个阻止拷贝动作的base class,子类尝试拷贝动作时,会调用base class的对应函数,而此调用会被编译器拒绝。
class Uncopyable {
protected:
//允许子类derived对象构造和析构
Uncopyable() {};
~Uncopyable() { };
private:
//阻止子类copying
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};