C++ 代码规范

版权、文件声明规范(Q:什么要申明版权以及说明?)

      这个更多意义在于以后的维护,以及代码阶段的版本控制。当项目进入成熟阶段之后,后期维护工作会占据很大的精力

      考虑到不同客户类型的需求,往往“类似功能”模块会有好几个,有些模块也不经常改动,因此很容易忘记,良好的习惯,就是增加对应的注释。

1、版权和版本的声明

【规范1-1-1】 C/C++在头文件(.h)需进行版权、版本、作者等声明。

版权和版本的声明位于头文件和源文件的开头(参见示例),主要内容有:

版权信息
文件名称,摘要
当前版本号,作者,日期

/**
*****************************************************************************
*  Copyright (C), 2010-2019, GOSUN CL
*
*  @file    文件名(包含后缀名)
*  @brief   简要描述本文件的内容
*
*  @author  作者
*  @date    输入日期
*  @version 版本号
*
*  (若本次是第一个定型版本,可以省略下列Note内容)
*
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note
*
*****************************************************************************
*/


///以下是例子,供参考///

/**
*****************************************************************************
*  Copyright (C), 2010-2019, GOSUN CL
*
*  @file    CLUtility.h
*  @brief   各种通用小功能/通用文件操作
*           1. 小功能:新增 正则匹配/查找下一个字符/文件夹(全部)创建
*           2. 重新封装: CECDateTime/CECString/CECFile/CECLogFile/
*           3. 新增功能模块:CECRunLog(运行日志)/CECReadIni(ini文件读取)
*
*  @author  gu
*  @date    2019-07-18
*  @version V1.1.0 20190718
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2018-2-15   1.创建
*
*****************************************************************************
*/

2、函数声明

【规范1-2-1】 每个函数(除了构造跟析构可以不用),都应该在函数上方进行注释,注释内容要与实际内容含义保持一致。

PS:如果函数功能或输入参数等信息发生变化,需要及时修改注释信息

/**
*****************************************************************************
*  @brief   描述该函数主要功能
*
*  @param[in]     参数名(与函数里的参数一致)  参数的描述    in:表示输入类型
*  @param[out]    参数名(与函数里的参数一致)  参数的描述    out:表示输出类型
*  @param[in,out] 参数名(与函数里的参数一致)  参数的描述    in,out:表示既是输入又是输出
*
*  @return  返回值的说明
*  @author  (若与文件申明作者是不一样的,需要填写;否则,可以省略此项)  
*
*  (若本次是第一个定型版本,可以省略下列Note内容)
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2019-5-15   1.创建
*
*****************************************************************************
*/


///以下是例子,供参考///

/**
*****************************************************************************
*  @brief   对外接口,用于new创建对象(实例CRunLog)
*
*  @param[in]    strPath    存放路径(完整)
*  @param[in]    nMaxDay    最多存放多少天
*  @param[in]    nLenDay    每天多少长度(字节)
*
*  @return  CRunLog的实例,指针   
*----------------------------------------------------------------------------
*  @note 历史版本  修改人员    修改日期    修改内容
*  @note v1.0      gu          2019-5-15   1.创建
*
*****************************************************************************
*/

注释规范(Q:为什么要注释规范?)

      首先,统一的注释风格,代码看起来也非常清爽,后期代码走读的时候,也更关注逻辑本身。其次,使用doxygen等第三方工具导出说明文档,也是需要规范的注释,降低后期的工作量。

1、代码注释


1.1 注释应当准确、易懂,防止注释有二义性。

1.2 注释采用上方以及右侧两种方式

如果是右侧方式,建议 采用“///< xxxx”方式(在doxygen “//” 表示下一行注释)采用“///<”进行右侧

举例:右侧方式,注释

u_char   m_byPipe;       ///< 通道号

举例:上方方式,注释

//报文协议处理函数

void    DealWithData(const byte *pBuf, int nLen);

1.3 注释地方:

  1. 类成员(包含 成员变量、成员函数)
  2. 结构体(变量)
  3. 全局变量
  4. 静态变量
  5. 说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部以及源文件头部应进行注释。
  6. 函数头部应进行注释。
  7. 全局变量、成员变量、静态变量、常量要有的注释。
  8. PS:其他非“必须”注释的地方不是说不用注释。注释对于代码维护尤其重要,良好的注释能够体现一个软件的水平。

1.4 修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。

1.5 避免在注释中使用缩写,除非是业界通用或子系统内标准化的缩写

1.6 同一产品或项目组统一注释风格

1.7 避免在一行代码或表达式的中间插入注释

除非必要,不应在代码或表达中间插入注释,否则容易使代码可理解性变差

1.8 注释建议使用中文

建议使用中文用非常流利准确的英文表达,对于有外籍员工的,由产品确定注释语言应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议多使用中文,除非能注释语言不统一,影响程序易读性和外观排版,出于对维护人员的考虑,建议使用中文。

命名规范(Q:为什么要命名风格要统一?)

     命名就好比给你自己小孩取名字。小孩取名字,我们要关注 “姓”,“辈分”,“含义”,“男女偏好”,“简洁”,“好记,好理解”等等。

     所以取名字也是非常讲究的。(灵魂拷问:你爱你的孩子吗?你爱你的代码吗?既然爱,怎么不取个好点的名字呢!) 有些人会喜欢很长的信息来表示名字,动不动就几十个字符,这样也不好,你想,如果你小孩的名字也很长,你会喜欢吗?

命名分为 类(结构体)、函数、常量、全局变量、静态变量、成员变量、临时变量(参数)等命名规则,应遵循以下规则:

两个基本原则:

含义清晰,不易混淆;
不和其它模块、系统API的命名空间相冲突


1、命名

【规范3-1-1】 变量、参数用类型缩写+大写字母开头的单词组合而成。

变量的名字应当使用
“小写缩写类型字符”(参考表) + “名词”
“小写缩写类型字符”(参考表) + “形容词/动词” + “名词”

BOOL bRes;
int nDrawMode;

常见的缩写:

//缩写(前缀)类型 

b        Boolean /bool/BOOL
n        Integer /int
str      String,char[],CString
p        Pointer //指针
h        Handle
fn       Function    

//缩写单词

argument       可缩写为 arg
buffer         可缩写为 buff
clock          可缩写为 clk
command        可缩写为 cmd
compare        可缩写为 cmp
configuration  可缩写为 cfg
device         可缩写为 dev
error          可缩写为 err
hexadecimal    可缩写为 hex
increment      可缩写为 inc
initialize     可缩写为 init
maximum        可缩写为 max
message        可缩写为 msg
minimum        可缩写为 min
parameter      可缩写为 para
previous       可缩写为 prev
register       可缩写为 reg
semaphore      可缩写为 sem
statistic      可缩写为 stat
synchronize    可缩写为 sync
temp           可缩写为 tmp

变量命名容易分两种极端情况,一种特别长,一种特别短,这里需要注意。

例如:



int nPrivateDTCSaveToFile;    ///< 特别长,意思够丰富,但啰嗦
Cfile file;                   ///< 缺少必要信息,等于没有任何含义。


建议:变量命名采用2-3个单词。

关于缩写的约束:
【规范3-1-2】 禁止使用拼音首字母进行创造性缩写。除了行业性缩写用语可以用缩写,其他不建议用缩写。

例如:WH(维护),这样的用法是禁止的

行业性缩写,例如:GYK(轨道车运行控制)、CIR、MMI、DMI,这些有些是英文缩写,有些是中文缩写,但已经形成行业用语,可以采用大写缩写方式。

2、静态变量

【规范3-2-1】 静态变量加前缀s_(表示static)。

void Init()
{   
    static int s_nAllValue; //静态变量 
}

//如果静态变量又是全局或成员变量(不推荐这样操作),应该采用如下方式: 

 ms_nVal
 gs_nVal

3、全局变量

【规范3-3-1】 如果需要定义全局变量,则使全局变量加前缀g_(表示global)

int g_nManyPeople; ///< 全局变量
int g_nMuchMoney;  ///< 全局变量

4、类成员变量

【规范3-4-1】 类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名。

void Object::SetValue(int nWidth, int nHeight)
{
   m_nWidth = nWidth;
   m_nHeight = nHeight;
}

5、常量

【规范3-5-1】 常量全用大写的字母,用下划线分割单词。

//C++ 写法如下:(不允许 #define 方式,不允许混合写法)

const int MAX_DATALEN = 100;
const int MIN_LENGTH = 100;

6、类名、函数名

【规范3-6-1】 类名用波峰式的单词组合而成。

class RunWorker  ///< 类名称可能是某些功能的集合也可能是某个类型,建议名词

【规范3-6-2】 函数名首字母小写,后面大写

公共成员函数:

public:
      int sendData()   ///< 函数一般来说表示具备什么功能,因此建议采用动名词组合

私有成员函数: 前面加_

private:
      int _sendData()   ///< 函数一般来说表示具备什么功能,因此建议采用动名词组合

7、结构体命名

【规范3-7-1】 自定义结构体名称要以“_tag”为开头。

typedef struct _tagWorkerSet
{           
    byte byPipe;   
    byte byIsCAN;
    byte byClass;
};

8、枚举类型 命名

[规范3-8-1]

枚举类型采用全大写的字母,用下划线分割单词。

enum OPER_RES
{
    OPER_SUCCESS = 0,           ///< 成功
    OPER_COPYING = 1,           ///< 正在复制(上次拷贝没结束)
 
    OPER_FREE = 3,              ///< 空闲
    OPER_DIR = 100,             ///< 操作文件夹(内部)
    OPER_FILE = 101,            ///< 操作文件(内部)
 
    OPER_ERROR = -1,            ///< 普通错误
    OPER_OPENDIR_ERROR = -2,    ///< 打开文件失败
    OPER_NO_DIR = -3,           ///< 无效文件夹(不存在,或路径错误)
    OPER_NO_COPYFILE = -4       ///< 不需要拷贝,文件都认为相同
};

代码行为规范  

1、总体原则

1.1 清晰第一

清晰性是易于维护、易于重构的程序必需具备的特征。代码首先是给人读的,好的代码应当可以像文章一样发声朗诵出来。一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。

1.2 简洁为美

简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。写的代码越多,意味着出错的地方越多,也就意味着代码的可靠性越低。因此,我们提倡大家通过编写简洁明了的代码来提升代码可靠性。

1.3 废弃代码要及时清除(没有被调用的函数、变量以及空格和不必要的注释等 )

程序中的废弃代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、维护等造成不必要的麻烦。

1.4 重复代码应该尽可能提炼成函数

2、运算符与表达式

【规范4-2-1】 如果代码行中的运算符比较多(超过2个),用括号确定表达式的操作顺序,避免使用默认的优先级。

【规范4-2-2】 如果是算术运算符(例如:加 减 乘 除)且超过2个,可以不用括号进行确定操作顺序。

【规范4-2-3】 如果全部是相同的运算符且超过2个,可以不用括号进行确定操作顺序。

为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。

nGetData = (nHigh << 8) | nLow;   ///< 用括号表示优先级
 
if ((bResR | bResG) && (bResR & bResB))


PS:
&& 操作符(同理||操作符,也是有存在类似问题),关系是当前面条件为false时,后面的是不会被执行。因此不推荐以下做法:

if(bRes && CallFun()) ///< 此时若bRes为false,那么CallFun是不会被执行

这里也很容易出错,往往也很难理解,甚至不同编译器对优先级理解不一样。

3、if 语句

3.1 布尔变量与零值比较

【规范4-3-1】 不可将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。

根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。
TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。

//假设布尔变量名字为bFlag,它与零值比较的标准if 语句如下:

if (bFlag)  // 表示flag 为真
if (!bFlag) // 表示flag 为假


 
//其它的用法都属于不良风格,例如:
 

if (bFlag == TRUE)
if (bFlag == 1 )
if (bFlag == FALSE)
if (bFlag == 0)


3.2 整型变量与零值比较

【规范4-3-2】 应当将整型变量用“==”或“!=”直接与0 比较。

//假设整型变量的名字为nValue,它与零值比较的标准if 语句如下:

if (0 == nValue)
if (nValue != 0)



//不可模仿布尔变量的风格而写成

if (nValue) ///< 会让人误解nValue 是布尔变量
if (!nValue)

3.3 浮点变量与零值比较

【规范4-3-3】 不可将浮点变量用“==”或“!=”与任何数字比较。

//千万要留意,无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。

//假设浮点变量的名字为fData,应当将
if (0.0 == fData) ///<  隐含错误的比较
 
//转化为
if ((fData >= -EPSINON) && (fData <= EPSINON))


//其中EPSINON 是允许的误差(即精度)。
 

3.4 指针变量与零值比较

【规范4-3-4】 应当将指针变量用“==”或“!=”与NULL 比较。

指针变量的零值是“空”(记为NULL)。尽管NULL 的值与0 相同,但是两者意义不同。假设指针变量的名字为pPen,它与零值比较的标准if 语句如下:

if (NULL == pPen) ///< pPen 与NULL 显式比较,强调pPen 是指针变量
if (pPen != NULL)
//或(前提必须符合命名规范)
if (pPen) 
if (!pPen)
 
//不要写成
if (pPen == 0)   ///< 容易让人误解p 是整型变量
if (pPen != 0)


4、switch 语句

【规范4-4-1】 每个case 语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。

【规范4-4-2】 若某个case不需要break一定要加注释声明,便于明白是故意不加而不是忘记。

【规范4-4-3】 不要忘记最后那个default 分支。即使程序真的不需要default 处理,也应该保留语句default: break;

switch(nParam)
{
    case NOPER_UPDATE:     ///<升级状态
        DoUpdate();
        break;
    case NOPER_RUN:        ///<开始运行
        RunApps();
        break;
    default:
        break;
}

5、循环语句(for / while)

【规范4-5-1】 为了防止循环失去控制,尽量不在循环体内修改循环条件变量,首先是规避,然后如果无法规避,必须注释清楚。

for (int i = 0; i < NMAX_RUNSTATE; i++)
{
    if(pMain->m_lsAppRun[i] != NULL)
    {         
         //...
    }
    else
    {
         i = 0;  ///< 虽然此处理论上不存在,但如果出现,必然是死循环
    }
}


6、关于空格、空行


【规范4-6-1】 连续超过10行的代码必须要添加空行。

原则:
1、 不同性质的代码(或变量)之间要有空行
2、 相同性质的代码应该放在一起,超过10个的,也要添加空行
3、 性质是指 属性、含义、功能相似等条件,可以根据实际情况来区别    

  //初始化
    m_byKeyUsed = 0x00;
    m_byKeyBit7 = 0x80;
    m_nCountSend = 0;
    m_byLastKey = 0x00;
 
    //读取数据(采用定时器读取数据,不采用事件,方便移植到linux)
    m_pRecvTimer = new QTimer(this);
    m_pRecvTimer->setInterval(50);
    connect(m_pRecvTimer,SIGNAL(timeout()),this,SLOT(ReadKeyCom()));
 
    //发送数据
    m_pSendTimer = new QTimer(this);
    m_pSendTimer->setInterval(50);
    connect(m_pSendTimer,SIGNAL(timeout()),this,SLOT(WriteKeyCom()));
 
    m_pSendTimer->start(50);
    m_pRecvTimer->start(50);
 
    //显示刷新
    ShowToObj(m_byNowKey);
    //打开串口
    OpenKeyCom();

【规范4-6-2】 关键字之后要留空格。
像 const、virtual、inline、case 等关键字之后至少要留一个空格,否则无法辨析关键字。
像 if、for、while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。

【规范4-6-3】 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,的前后应当加空格。 *
如“=”、“+=” “>=”、“<=”、“+”、“”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。

void OnReadFile(int nX, int nY, int nZ); ///< 良好的风格
void OnReadFile (int nX,int nY,int nZ);  ///< 不良的风格
if (nYear >= 2000) ///< 良好的风格
if(nYear>=2000)    ///< 不良的风格

函数规范


     Q:函数有哪些地方需要注意的?

     函数除了名称之外,还有参数,以及必要输入,输出等约束

     何为圈复杂度?

1、参数传递效率

【规范5-1-1】 如果输入参数以值传递的方式传递对象(类型为类、结构体),则用“const & ”方式来传递

这样可以省去临时对象的构造和析构过程,从而提高效率。对于类型为常规类型,如char int等,建议采用直接传值的方式。例如:

//C++ 写法要求:

GetErrorInfo(const _tagRunInfo &stRun)

2、出入口检查,安全

【规范5-2-1】 在函数体的“入口处”,对参数的有效性进行检查。

int CLocalSocket::SendData(const byte byOrder,const byte *pbyBuf,const int nLen)
{
    if((nLen > MAXLENGTH_FRAME) || (nLen < 0) || (pbyBuf == NULL))
    {
        return -1;
    }
 
//....
}

参数必须要判断:
1. 指针类型的参数

对于其他类型 如 int,建议根据实际情况进行判断。

3、入口唯一

【规范5-3-1】 任何函数只能有一个入口;即只能使用函数调用的方式进入函数,不能使用goto等语句进入函数内部。

4、重复代码应该尽可能提炼成函数

【规则5-4-1】 重复代码应该尽可能提炼成函数

重复代码提炼成函数可以带来维护成本的降低。


5、代码行数要求

【规则5-5-1】 一个函数原则上要求代码行数不超过200行(包含注释、空行)。

代码行数比较多,说明开发人员缺乏进一步思考,仅关注功能实现。
代码行数比较多,意味着逻辑复杂、思路不清晰,存在代码隐患就比较高。
代码行要求200行以内已经是非常宽泛的要求了,实际应该在50行以内为最佳。

PS:
另外建议,单个 类(.cpp文件)或.c文件源代码行控制在1000行以内。否则就应该先考虑是否可以进行优化设计。

6、避免函数的代码块嵌套过深

函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”(比如,进入条件语句、进入循环„„)。应该做进一步的功能分解,从而避免使代码的阅读者一次记住太多的上下文。

7、对函数的错误返回码要全面处理

一个函数(标准库中的函数/第三方库函数/用户定义的函数)能够提供一些指示错误发生的方法。这可以通过使用错误标记、特殊的返回数据或者其他手段,不管什么时候函数提供了这样的机制,调用程序应该在函数返回时立刻检查错误指示。

例如:sql 函数的返回值,自定义了返回类型:

//SQL函数返回值
	enum ErroStatus
	{
		esOk = 0, //OK
		esInputInvalid, //输入无效
		esConnErro, //连接错误
		esConnFailed, //连接失败
		esSQLStmtInvalid, //SQL语句无效
		esBindIncomplete, //绑定不完整
		esBindFailed, //绑定失败
		esSQLObjErro, //SQL对象错误
		esSQLExcuteFailed, //SQL执行失败
		esFieldIndxInvald, //字段索引无效
		esResultErro, //结果集错误
		esResultNULL, //结果集为空
		esFetchFailed, //取出结果失败
		esFetchDefaultFailed, //取出结果默认值失败
		esSendFailed, //发送失败
		esTransStartFailed, //事务开启失败
		esTransSubmitFailed, //事务提交失败
		esTransRollBackFailed, //事务回滚失败
		esFileNotExist, //文档不存在
		esFileReadFailed, //文档读取失败
		esFileWriteFailed, //文档写失败
	};

内存使用规范


     Q:内存使用本身就是C++的精华,也是把双刃剑。

     除了细心一点,还要注意一些原则。否则出了事情,都是大事情。

1、防止产生“野指针”

【规范6-1-1】 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”,除非该指针变量本身将要消亡。

【建议】申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存;

delete m_pTxOri;
m_pTxOri = NULL;   ///< 及时变成NULL
 
delete[] m_pWriteBuf;
m_pWriteBuf = NULL;   ///< 及时变成NULL


野指针指向一个已删除的对象或未申请访问受限内存区域的指针。
与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。
对野指针进行操作很容易造成程序错误。

以下一些手段可以有效控制:

new delete 在一个函数内,最好是在一个屏幕可视范围内
new出来的对象,不要被其他对象进行引用
特别是结构体的成员或类成员是指针,要注意被引用的可能。
像例子的那样,一般也还好,比如这样,就麻烦:

byte *pBuf = m_pTxOriBuf;
 
//do somethings
 
delete[] m_pTxOriBuf;
m_pTxOriBuf = NULL;   ///< 及时变成NULL
 
//.....
 
//...
//do somethings
if(pBuf != NULL)
{
    //此时pBuf 是野指针了,比较容易忽视这一点。
    pBuf[0] = 0;
}

再来一个比较隐蔽的例子:

void CallFun1(byte *pBuf)
{
//....
    delete[] pBuf;
    pBuf = NULL;
}
 
 
void CallFun2(byte *pBuf)
{
//....
    pBuf[0] = 0x00;
    //....
}
 
 
 
void main()
{
    byte *pTxBuf = new byte[250];
 
    //do somethings
 
    CallFun1(pTxBuf);
 
    //.....
 
    //...
    //do somethings
    if(pTxBuf != NULL)
    {
        //此时pBuf 是野指针了,比较容易忽视这一点。
        CallFun2(pTxBuf);
    }
}

2、数组越界

【规范6-2-1】 避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

数组越界会带来不可预期的问题,也有可能运行时不报错。所以经常会犯错,特别是一些莫名其妙的错误,往往是越界导致。

//eg1:
char strName[20];
for(int i = 0;i <= sizeof(strName);i++)  ///< 已经越界
{
    strName[i] = 0x30 + i;
}
 
 
 
//eg2:
const int MAX_NMAE_LEN = 16; ///< 原先是10,由于协议改变,现在变成16
char strName[10];           ///< 此处未及时改过来
for(int i = 0;i < MAX_NMAE_LEN;i++)  ///< 已经越界
{
    strName[i] = 0x30 + i;
}

容易导致越界:
数组下标是变量,当特殊情况下,变量可能变成-1或很大一个值
协议内容定义变化,原先协议是没问题,后来协议增加了,导致问题出现

3、内存泄露

【规范6-3-1】 当对象消亡时确保指针成员指向的系统堆内存全部被释放,否则会造成内存泄露。

另外应当注意,如果是用第三方一些API或机制,一定要注意是否有内存泄露的风险。比如在GDI中,资源如果不回收,那么就会内存泄露。

Image* pImage = Image::FromFile(L"E:\\bac.bmp");
Graphics g(pDC->m_hDC);
g.DrawImage(pImage,0,0);
 
 
delete pImage;             ///< 必须,否则泄露。这个取决API函数的特性
g.ReleaseHDC(pDC->m_hDC);

【规范6-3-2】 动态内存的申请与释放必须配对(特殊应用可特殊处理,但不建议把特殊当成常规),不需要的内存应被及时释放,防止内存泄漏。
并且new、delete和new[]、delete[]要成对使用。特别要注意中间return。

不匹配也会导致内存泄露的。

封装规范:(待完善)
 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值