读书人

C++代码轨范和风格

发布时间: 2012-07-28 12:25:13 作者: rapoo

C++代码规范和风格
代码规范

注释
文件头
C++代码轨范和风格

类说明在类声明之前

C++代码轨范和风格

方法和变量

1. 使用C++风格的注释方式, C语言的/*…*/方式避免使用

C++代码轨范和风格

2. 大量代码的注释使用宏定义

C++代码轨范和风格

3. 枚举类型

C++代码轨范和风格

分隔带
////////////////////////////////////////////////////////////////////////////////////////////
头文件规则

1. 头文件包含h, hxx和inl,分别表示

*.h : Header File

*.hxx : Private Header File

*.inl : Inline File

2. 头文件以文件头注释开始[参考注释小节的文件头]

3. 之后为防止多次编译的宏定义和命令行

4. 头文件需要包含的其他文件[*.inl不能包含其他文件]

#include <XXX> --- 包含系统级别的头文件

C++代码轨范和风格

#include “XXX” --- 包含项目级别的头文件

C++代码轨范和风格

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
0

class 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已被删除

//...

在没有进行第一次赋值操作之前:

C++代码轨范和风格

在进行第一次赋值操作之后:

C++代码轨范和风格

当b对象被释放, b原来申请的内存没被释放, 反而将a对象的内存释放了, 这个时候a对象的指针变量变成了野指针[默认的赋值操作代码由编译器帮我们简单的实现了]

再看下面的代码:

//void donothing(string b)

//{

// // ...

//}

string a = "the truth is out there";

donothing(a);

当方法donothing调用完成, a对象的指针变量变成野指针

C++代码轨范和风格

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); // ?

}

C++代码轨范和风格

对于类的复制构造和赋值操作符, 我们在开发中要注意的风格是:

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
收藏了。

读书人网 >C++

热点推荐