一、命名规范:使用能明确描述一个变量或对象的名字。函数名应该包含一个表示函数行为的动词。
1. Linux风格(使用下划线分隔单词,全小写命名)
int some_variable;
float bar_weight;
unsigned int users_number;
bool is_engine_started;
double circle_area;
double m_circle_area; //类的私有变量, 增加 m_ 前缀
void* psome_void_pointer;
const int USERS_NUMBER;
int i, j, n, m, tmp; //本地循环变量, 临时变量
namespace mynamespace;
vector users;
vector user_names;
class SomeClass;
int do_work(int time_of_day);
float calculate_radius(const Circle& rcircle);
int start_io_manager();
int open_dvd_player();
- windos风格(MFC程序,增加匈牙利式前缀表示变量类型)
INT nSomeVariable;
FLOAT fBarWeight;
DWORD dwUsersNumber;
BOOL bIsEngineStarted;
DOUBLE dCircleArea;
DOUBLE m_dCircleArea; //类私有变量,增加m_前缀
PVOID pSomeVoidPointer;
const INT USERS_NUMBER;
int i, j, n, m, tmp; //本地循环变量,临时变量
namespace MyNameSpace;
vector<int> nUsers;
vector<char *> pszUserNames;
class CSomeClass;
INT DoWork(INT nTimeOfDay);
FLOAT CalculateRadius(const& Circle rCircle);
INT StartIOManager();
INT OpenDvdPlayer(); //如果缩写超过两个字母,则不要全大写
- .NET平台(匈牙利命名法,去掉前缀);
int someVariable;
float barWeight;
unsigned int usersNumber;
bool isEngineStarted;
double circleArea;
double circleArea; //表示类私有变量,以this->circleArea形式使用
void^ someVoidPointer;
const int UsersNumber;
int i, j, n, m, tmp; //本地循环变量,临时变量
namespace MyNameSpace;
array<int> users;
array<String> userNames;
class SomeClass;
int DoWork(int timeOfDay);
float CalculateRadius(const& Circle circle);
int StartIOManager();
int OpenDvdPlayer(); //如果缩写超过两个字母,则不要全大写
二、
1. Tab键用8个空格,所以缩进也是8个字符,这样是为了清除地表示一个代码块的开始和结束
2. 空格,大括号和圆括号在函数和类定义的时候,把大括号另起一行会让代码看起来更清晰 : 把开括号放在行末闭括号放在行首,目的是为了减少空行的数量。
3. 防止重复包含:用宏把头文件内容包装起来防止被重复包含
#ifndef Foo_h
#define Foo_h
// ... Foo.h 文件内容
#endif
三、类设计
当你声明一个类时,要记住如果你没有提供这些函数的定义,C++编译器会自动帮你提供:
构造函数
拷贝构造函数
赋值操作符
取地址操作符(const的和非const的)
析构函数
//声明一个类
class SomeClass
{
};
//你将自动拥有这些函数
class SomeClass
{
public:
SomeClass() { } //构造函数
~SomeClass() { } //析构函数
SomeClass(const SomeClass &rhs); //拷贝构造函数
SomeClass& operator=(const SomeClass& rhs); //赋值操作符
SomeClass* operator&(); //取地址操作符
const SomeClass* operator&() const; //const取地址操作符
};
如果类在构造函数中分配了内存并且在析构函数中才释放这些内存而又不想提供拷贝构造函数和赋值操作符的话,把拷贝构造函数和赋值操作符声明为私有的(并且不要实现它们),这样可以防止意外地复制对象导致内存被重复释放。
按照public,protected, private的顺序声明类成员。这样类的使用者能够立即看到需要使用的公共接口。
#ifndef ClassName_h
#define ClassName_h
class ClassName
{
public:
ClassName();
ClassName(const ClassName& classname);
virtual ~ClassName();
// Operators
const ClassName& operator=(const ClassName& classname);
// Operations
int do_work();
int start_engine();
int fire_nuke();
// Access
inline unsigned int get_users_count() const;
// Inquiry
inline bool is_engine_started() const;
protected:
//protected functions for descendant classes
private:
//private data and functions
unsigned int m_users_count;
bool m_is_engine_started;
};
// Inlines
inline unsigned int SomeClass::get_users_count() const
{
return m_users_count;
}
inline bool SomeClass::is_engine_started() const
{
return m_is_engine_started;
}
#endif ClassName_h
把内联函数放在头文件中,并且要放在类定义外面,保持类定义的简洁。
四、正确使用const
1. 把一个成员函数声明为const表示这是个“只读取”函数,也就是说调用这个函数不会修改它所属的对象。
2. 可以把一个成员变量声明为 mutable 这样就可以在const成员函数中修改它。
3. 可以防止一个已经赋值的指针被重新赋值指向另外一个地方,类似于引用。
4. 可以同时声明一个const对象和const指针
5. 如果类有提供()或[]操作符,则也提供它们的const版本,让”只读”对象也能调用。
五、函数返回值
函数返回各种类型的返回值;常见的一种就是返回一个表示函数成功与否的值:这种返回值可以描述成整型的错误码(负数 = 失败, 0 = 成功) 或者是“是否成功的”布尔值(0 = 失败, 非零 = 成功)。
如果函数名是一个命令式词组,那么这个函数应该返回错误码,否则如果函数名是一个谓词,那么应该使用“是否成功的”布尔值。
int start_engine(); //0 = 成功, 负数 = 错误码
bool is_engine_started(); //true = 成功, false = 错误
六、其他
1. 把 & 和 * 跟类型放在一起,不要跟变量放在一起。这样让变量类型看起来更显眼一些。
2. 把非负变量定义成 unsigned 类型(例如:使用 unsigned int 表示数组长度而不是用 int 表示数组长度)。这样有助于避免意外地被赋值一个负数。
3. 引用是一个类似于 int * const p 的指针,一旦赋值,就不能重新赋值。
4. 这样声明一个 const 的Foo类型的指针的指针:const Foo* const* ppfoo
5. 总是在函数声明中提供形式参数:
void set_point(int, int); //迷惑, 参数是什么?
void set_point(int x, int y); //正确的声明
6. 指针跟0的比较(译注:是指测试指针是否等于0,不是测试是否空指针,空指针应该用NULL)
7. 类对象做函数参数时,选用传指针或者引用。传值调用时会产生一个这个对象的拷贝,如果对象占用大量内存,这样会带来不良的后果。
//避免这样做, 应该传指针或者引用
BigObject modify_it(BigObject big_object)
{
//会产生一个big_object的本地拷贝
…
return big_object;
}
//良好的习惯
void modify_it(BigObject& big_object)
{
//no copy is created, you can modify the object as usually
//不会发生复制,你可以照常修改big_object
}
8. 面向对象技巧
永远不要返回类的私有数据的指针或者非const引用。不要打破封装原则。
9. 总是把公共类的析构函数声明为virtual。如果你从某个没有 virtual 析构函数的类继承了子类,然后你把指向子类的指针转换成指向父类的指针,这时对这个父类类型的指针调用delete则只会调用父类的析构函数.
10. 使用友员。如果你正在开发一个库,你不想你的库用户使用某个类,但是你的公共类又需要访问这个类的私有成员时,提供一个公共访问接口是多余的(译注:作者意思是既然这个类不给库用户使用,公共接口就没必要了,持保留意见)。把你的公共类声明为这个类的友员,这样你就可以访问他的私有数据了。
class MainClass
{
…
void some_function();
…
HelperClass* phelper_class;
…
};
void some_function()
{
…
int i = phelper_class->m_variable; //你可以在MainClass中访问HelperClass的私有成员了
phelper_class->m_variable = 10;
…
}
class HelperClass
{
friend class MainClass;
…
private:
int m_variable;
…
};