C++编码规范
游戏服务端
目录
C++编码规范
一、通用命名
二、格式
三、类.
四、作用域
五、C++特性
六、协议
七、注释
八、其它
一、通用命名
1.1 简写规则:常用单词缩写简写,如dns、id等,否则最好采用单词全称书写
1.2 局部变量:使用“_”连接,所有单词小写,如user_id
1.3 函数名:首字母小写,其余单词首字母大写,如addTask()
1.4 文件名:全部字母小写,单词中间采用“_”连接,如果内部定义类,请与类名一致
1.5 类名:全部单词均使用首字母大写,如SceneTemplate,对应的文件名为scene_template.h
1.6 宏定义:所有字母大写,单词中间采用“_”连接
1.7 枚举:所有字母大写,如果放于某个类内,第一个单词最好使用类名缩写,如定义了class SceneTemplate {
Enum {
ST_XXX_XXX = 0,
ST_XXX_CCC = 1,
}
}
二、格式
2.1 代码文件内不允许存在tab键,均采用4个空格代替,每次缩进4字节
2.2 每行代码不宜太长,100字符以内(21.5寸显示器屏幕对半分宽度),如果有换行情况,注意美观对齐,如方法名参数对齐,for循环内部变量对齐等
2.3 中括号使用,第一个{均在前一行末尾,如
if (xxx) {
……
} else {
……
}
PS:即使if内部只有一行代码,也需要用{}括起来不允许写成“if () xxx;”且,”} else {”必须在同一行
void func() {
……
}
2.4 每一行末尾不要存在空格符号,且每一空行不要存在空格符,vim可以使用set list查看
2.5 命名空间内的代码不要缩进,且内嵌的命名空间也无需缩进,如
namespace Foo{
namespace Bar{
void func() {
……
}
class {
…
};
}
}
三、类
3.1 头文件
a) 头文件定义均采用预处理宏模式,防止重复包含头文件出现重定义错误
#ifndef XXX_H__
#define XXX_H__
……
#endif
其中XXX_H__均使用 命名空间_文件名_H__,文件名单词之间均使用“_”连接
b 成员变量
i) 均以m_开头,如m_id
ii) 包含多个英文单词组合而成是第二个单词首字母大写规则,如m_userId
c) 所有的类均在某一命名空间内,不允许单独存在
d) 根据实际需求使用public/protected/private,也是按照这顺序声明,尽量不要把成员变量暴露出来
e) 某些简单的func可在头文件直接实现,如:
void vip(int vip) { m_vip = vip; }
int vip() { return m_vip; }
PS:注意中括号两边均有空格
f) 如需引入其它类采用前置声明,不引入头文件,在cpp文件引入
g) 方法根据实际需求定义的时候需要归类
3.2 cpp文件
a) 方法均在命名空间内实现
b) 引入头文件顺序
i)当前类的头文件
ii)系统库
iii)自定义头文件(均按26个字母顺序编写)
PS:此3项中间采用空行分割
3.3 构造函数不要有比较复杂的数据初始化,及虚函数调用,如有需求使用init()方法封装,对象创建后调用初始化
3.4 当成员仅有数据时使用struct,无需使用class
3.5 面向对象三要素:封装,继承,多态。继承能减少代码的耦合度,根据适当的场景使用,以高内聚低耦合为准则,不要出现过度封装的情况
四、作用域 namespace
4.1 代码无需缩进
4.2 类需前置声明
4.3 除#include及引入非此作用域空间声明外,其余所有的代码均在namespace范围内
五、C++特性
5.1) ++与--操作,如果不考虑返回值均使用前置,效率较高,减少一次数据拷贝过程,尤其是在迭代器里面操作时
5.2) 多线程并发下,对容器的读写赋值操作必须加锁,同时一个类里面只需声明一个锁即可,无需根据对象区分,多个锁情况下使用错误会发生死锁情况
5.3) 局部变量声明需要初始化,如果不初始化部分平台编译器可能给的默认值不一样,如int tmp;不同编译器tmp可能为0,也可能为随机值,int类型在声明为全局变量的时候默认值为0
5.4) 引用传参,如void fun(string& str); 如果确定str不变,必须加上const修饰。大部分时候使用引用比指针更易于代码阅读,如void fun(int* val),val修改时必须用++(*val)等类似操作
5.5) 禁止使用RTTI机制,防止switch代码到处都是,不利于维护
5.6) auto
a) 只用于局部变量声明,如:
std::map<int, short> map1;
map1.insert(std::pair<int, short>(1, 2));
map1.insert(std::pair<int, short>(2, 3));
map1.insert(std::pair<int, short>(3, 4));
for (auto iter = map1.begin(); iter != map1.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
b) 为便于阅读,当声明与使用相隔较远时,请使用变量类型全称,如
std::map<int, short> map1;
………………………………
………………………………
………………………………
std::map<int, short>::iterator iter = map1.find(1);
六、协议
a) 协议号,C2S采用奇数,S2C采用偶数,按自增顺序定义
b) 每一个servlet对应的协议号需分段处理
c) 注意协议类内部成员变量类型,根据双方协定顺序读取/写入字节,以免错乱
七、注释
遵循原则
a) 原则上应删除已废弃代码逻辑,无需保留
b) 单行注释//,放于当前行首字母位置
c) 纯文本说明,使用//,对齐对应代码
d) 多行代码块注释/*……*/,“/*”与“*/”单独一行显示 如:
/*
code
*/
八、其它
8.1 容器基本特性,分为两大类,使用时应合理选择
a) 顺序容器:vector、list、deque、string、stack、queue、priority queues,其中后三个为适配器
b) 关联容器:set、multiset、map、multimap、bitset、hash_set、hash_map、hash_multiset、hash_multimap
c) 几种常用容器比较
数组:vector,deque
链表:list
红黑树:map,multimap,set,multiset
8.2 boost库使用shared_ptr时应注意
a) 循环引用时需引入weak_ptr,防止因循环引用而导致的内存泄漏问题
b) 不可在调用函数中直接new,如func(shared_ptr<int>(new int), g());不同编译器执行的参数顺序可能不同,如果g()发生异常就有可能会出现内存泄漏
c) 初始化的时候必须new出一个对象,不可两个智能指针使用同一个对象初始化,正确写法:
boost::shared_ptr<A> p1(new A);
boost::shared_ptr<A> p2(new A);
错误写法:
A* a = new A;
boost::shared_ptr<A> p1(a);
boost::shared_ptr<A> p2(a);
造成2次析构
8.3 boost类型转化函数boost::lexical_cast使用
boost::lexical_cast<char>(str),char本身就只存储一个字符,如果str多于一个字符会转化失败,如str=”10”,正确写法char c = boost::lexical_cast<int>(“10”);