C++代码规范和风格
代码规范
注释
文件头
类
类说明在类声明之前
方法和变量
1. 使用C++风格的注释方式, C语言的/*…*/方式避免使用
2. 大量代码的注释使用宏定义
3. 枚举类型
分隔带
////////////////////////////////////////////////////////////////////////////////////////////
头文件规则
1. 头文件包含h, hxx和inl,分别表示
*.h : Header File
*.hxx : Private Header File
*.inl : Inline File
2. 头文件以文件头注释开始[参考注释小节的文件头]
3. 之后为防止多次编译的宏定义和命令行
4. 头文件需要包含的其他文件[*.inl不能包含其他文件]
#include <XXX> --- 包含系统级别的头文件
#include “XXX” --- 包含项目级别的头文件
5. 命名空间
6. 为保证头文件的整洁明了, 内联的请尽量在*.inl文件中完成, *.inl放在最后
7. 头文件可以只声明一个类, 也可以按关系需要声明多个类
8. 以下是一个头文件按1-7条的模版
/////////////////////////////////////////////////////
// Volcano Orca Lord(VOL) SDK //
// Copyright(C) VOL Studio. All RightsReserved //
// Header File : codec.h //
// Author : orca.vol@gmail.com //
// Create : 2007-03-06 version 0.0.0.1 //
// Update : 2012-02-28 version 0.0.4.8 //
// Detail : codec //
/////////////////////////////////////////////////////
#ifndef __CODEC_H__
#define __CODEC_H__
#pragma once
#include "tstring.h"
///////////////////////
namespace VOL //{ //
///////////////////////
////////////////////////////////////////////////////
// CBase64
#ifdef
0class NOVTABLE CBase64 :public MObject
{
public:
static bool Encode(CString& strIn,CString& strOut);
static bool Decode(CString& strIn, CString& strOut);
};
#endif
////////////////////////////////////////////////////
#include "codec.inl"
///////////////////////
} // namespace VOL //
///////////////////////
#endif //__CODEC_H__
源文件规则
1. 源文件包含cpp和cxx, 分别表示
*.cpp : Source File
*.cxx : Private Source File
2. 源文件以文件头注释开始[参考注释小节的文件头]
3. 源文件需要包含的其他文件
#include <XXX> --- 包含系统级别的头文件
#include “XXX” --- 包含项目级别的头文件
4. 使用命名空间的语法说明
5. 源文件内容
6. 以下是一个头文件按1-5条的模版
/////////////////////////////////////////////////////
// Volcano Orca Lord(VOL) SDK //
// Copyright(C) VOL Studio. All RightsReserved //
// Private Source File : streamfile.cxx //
// Author : orca.vol@gmail.com //
// Create : 2007-03-06 version 0.0.0.1 //
// Update : 2012-02-28 version 0.0.4.8 //
// Detail : codec //
// //
/////////////////////////////////////////////////////
#include"inc.h"
#include"memmgrdef.hxx"
#include"platform.hxx"
#include"memaid.hxx"
#include"streamfile.hxx"
///////////////////////
usingnamespace VOL; //
///////////////////////
/////////////////////////////////////////////////////
// CFileReadStream
ULLong CFileReadStream::Read(void* pV, ULLong ullLenBytes)
{
//…
}
命名规范
请按照微软的匈牙利命名规范
注意和建议
代码规范对刚开始实行可能会有所不习惯, 但给予团队内部代码可读性和维护性而言是个极大的提高, 所以请先努力按规范编写代码, 为高质量完成项目打下基础
代码风格
如果说代码规范是团队制定的统一代码标准的话, 代码风格就可以理解为是C\C++语言效率上提高的统一经验积累.
代码风格的重要性纵观计算机语言的发展, 我们提取出每个时期的代表语言,
1. 二进制编码
2. 汇编语言
3. C语言
4. C++语言
我们从代码执行效率上去判断, 可以认为上面的先后顺序: 1 = 2 >= 3 >= 4, 语言的发展反而使执行效率降低了, 但是, 效率的牺牲, 换来的是更高的提升:
1. 代码的可读性
2. 代码的可维护性
3. 代码质量
4. 开发效率
我们要讨论的风格, 就是在遵循上面提升的基础上, 让语言的执行效率能保持最优化而言的.
代码风格的重点C\C++语言是一种自由度很高的语言, 我们不可能全面的去讲解每个知识点, 这里提取出最重要也是最常用的亮点来讲解:
1. 指针有关
2. 类有关
指针有关
和指针有关的, 就是指针变量和其指向的内存之间的关系了, 这个也是所有使用C\C++最最容易出问题的地方. 有人说, 反正程序能跑完, 所有泄露的内存都会被系统回收的, 这个观点是没错误, 但是我们要考虑到如果是服务器程序, 开了就需要运行几年的, 不知道那天,所有内存被你的程序吃完了, 又或者临时变量将栈耗光了, 你的程序还能跑完吗?
所以, C\C++语言, 指针和内存的关系永远是重点. 从指针的内存申请方式, 可以简单的归纳为以下3种:
1. 静态存储的, 一般为全局变量或者类的静态成员变量
2. 从栈(stack)中申请的临时内存, 临时类对象, alloca的申请
3. 从堆(heap)中申请的,malloc或者new
上面3中情况, 1和2有系统自动回收指针变量对应的内存空间, 我们可以不用关注; 我们的重点是第3种情况, 这种情况下, 请永远记住以下的检测过程:
1. 指针变量出作用域变无效 ≠ 指针变量指向的内存被释放
2. 指针变量指向的内存被释放 ≠ 指针变量变无效(NULL)
举个例子:
void Convert(char* pszTxt)
{
int nLen = strlen(pszTxt);
char*pszTmp =new char[nLen+ 1];
for(int i = 0; i < nLen; ++i)
{
pszTmp[i] = pszTxt[nLen - i- 1];
}
pszTmp[nLen] = 0;
pszTxt= pszTmp;
}
// void control::GetContent(char*& p);
// 获取控件的内容,不会失败
void MyForm::Function(...)
{
char*pTemp = NULL;
control.GetContent(pTemp);
//…
Convert(pTemp);
control.Update();
}
对于指针变量和对应的内存, 我们在开发中要注意的风格是:
1. 判断申请内存返回情况, 内存耗尽了的情况还是有一定几率存在的
2. 指针变量初始化, 不要变成野指针
3. 指针变量的作用域
4. 指针变量指向的内存被释放, 建议将指针变量设计为NULL
类有关
类有关的代码风格, 我们可以参考Effective C++
这里重点讲解以下几个点:
1. 类的复制构造方法和赋值操作符[EC++, item 11]
2. 类的初始化列表[EC++, item 12, 13]
3. 基类的虚析构方法[EC++, item 14]
4. C++风格的类型转换
复制构造方法和赋值操作符1. 我们以一个简单的String开始举例讲解:
class String
{
public:
String(constchar* value);
~String(void);
private:
char*data;
};
inline String::String(constchar* value)
{
if(value != 0)
{
data = new char[strlen(value)+ 1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}
inline String::~String(void)
{
delete[]data;
}
以下代码执行:
//...
string a("hello"); // 定义并构造a
{ // 开一个新的生存空间
string b("world"); // 定义并构造b
//...
b = a; // 执行operator=, 丢失b的内存
} // 离开生存空间, 调用b的析构函数
string c = a; // c.data 的值不能确定, 因为a.data已被删除
//...
在没有进行第一次赋值操作之前:
在进行第一次赋值操作之后:
当b对象被释放, b原来申请的内存没被释放, 反而将a对象的内存释放了, 这个时候a对象的指针变量变成了野指针[默认的赋值操作代码由编译器帮我们简单的实现了]
再看下面的代码:
//void donothing(string b)
//{
// // ...
//}
string a = "the truth is out there";
donothing(a);
当方法donothing调用完成, a对象的指针变量变成野指针
donothing虽然什么都没做, 但是调用它时, 生成了一个临时对象b, 当b出donothing方法作用域也就不存在了, 当b对象被释放, 将a对象的内存释放了, 这个时候a对象的指针变量变成了野指针[默认的复制构造代码由编译器帮我们简单的实现了]
2. 在看下面例子
class base
{
public:
base(inti = 0);
virtualint getval(void)const;
private:
intdata;
};
inline base::base(int i)
: data(i)
{
}
inline int base::getval(void)const
{
returndata;
}
class derived :public base
{
public:
derived(inti = 0,int j = 0);
virtualint getval(void)const;
private:
intvalue;
};
inline derived::derived (int i,int j)
: base(i)
, value(j)
{
}
inline int derived::getval(void)const
{
return(base::getval() + value);
}
再看下面的代码:
void report(base b)// 系统默认在调用中帮我们生成了一个base类型, 不管实际对象是哪个
{
printf("the value is = %d\n", b.getval());
}
void main(void)
{
base b(10);
derived d(10, 51);
report(b); // OK
report(d); // ?
}
对于类的复制构造和赋值操作符, 我们在开发中要注意的风格是:
1. 类对象传递使用引用
2. 如果不想实现类的复制构造和赋值操作符, 请将它们声明为私有
3. 将基类虚析构直接声明为纯虚方法, 防止使用者参数不是引用类型时的安全
类的初始化列表类的初始化列表初始化效率比在类的构造方法里要好
类的初始化类表初始化变量顺序应该按变量声明的顺序来排列
1. 例子
class name
{
public:
name(constchar*str1, const char*str2);
private:
String strName;
String strAlias;
};
// 1
name::name(constchar*str1, const char*str2)
{
strName = str1;
strAlias = str2;
}
// 2
name::name(constchar*str1, const char*str2)
: strAlias(str2)
, strName(str1)
{}
// 3
name::name(constchar*str1, const char*str2)
: strName(str1)
, strAlias(str2)
{}
建议使用3
虚基类析构方法例子:
class base
{
public:
base(void);
~base(void);// virtual ~base(void);
};
inline base::base(void)
{
printf("Inbase()\n");
}
inline base::~base(void)
{
printf("In~base()\n");
}
class derived :public base
{
public:
derived(void);
virtual~derived(void);
private:
char*data;
};
inline derived::derived(void)
{
data = newchar[STR_LEN_MAX];
assert(data != 0);
data[STR_LEN_MAX - 1] = 0;
}
inline derived::~derived(void)
{
printf("In~derived()\n");
assert(data != 0);
delete[]data;
}
看以下代码:
void kill(base* pb)
{
deletepb;
}
int main(void)
{
derived * pd = new derived;
assert(pd != 0);
kill(pd);
return0;
}
基类请尽量使用虚析构方法
C++风格的类型转化const_cast
reinterpret_cast
static_cast
dynamic_cast
- 1楼Wentasy3天前 18:46
- 收藏了。










