|
Posted on 2009-04-23 15:44 向往 阅读(1066) 评论(4) 编辑 收藏 引用 所属分类: C/C++/Script
接触C++已经有五个年头了,多多少少在学习和工作的过程中有些感悟,遂一一记录,勉己勉人. 有少许字段是摘抄自网上的文章,但存档时只是随手黏贴,丢失了作者信息,无法列出,对此表示歉意,并感谢发扬分享精神的原作者. 此外,若以后有新的感悟将在此添加.有不当之处,恳请指正.
- 指针赋值时,要谨防指针的同值性.如:
1 void SharePtr::operator = (Ptr *ptr) 2 { 3 if(mPtr != ptr) // 防止指针同值.其中mPtr是SharePtr类中用来保存指针的变
量. 4 { 5 if(mPtr != NULL) 6 { 7 delete mPtr;
// 如果不是同值, 删除之前的指针. 8 mPtr = ptr; 9 } 10 } 11 }
- 不要忽视编译警告,有些警告是出现bug并导致程序crash的征兆。比如类型转化警告、数据未初始化警告都可能导致程序出错。为了以后减少调试和避免错误的几率,请重视编译器的警告信息。
- 当工程需要给Class加导出前缀(如:__declspec(dllexport))却有些没有加时,其他工程引用它将会出现
Unrezosle Symbol的错误.
- 若某个类需要作为父类,其析构函数必须声明为virtual,否正子类的析构函数无法调用,导致内存泄漏.
- 新增一个Class时,先定好接口,然后再编码.这样可以不用经常改动,减少编译时间.
- 要谨防数值越界.对于有些存储范围小的变量,必须先进行越界处理,然后再赋值.如:
// 未添加数据越界检测代码 int num = -1; unsigned char ch = num; // ch的值不是-1,而是255.因为num超出了unsigned char表示的范
围.
// 添加了数据越界检测代码 int num = -1; assert(num >=0 && num <= std::limit<unsinged char>::max()); // 若越界则弹出断言 unsigned char ch = num;
- 在宏里边尽量不定义变量,否则在外部调用两次时,有可能出现变量重复定义的错误.
如果确实要定义变量,可以改写成函数.
- 不要为模式而模式.使用设计模式只是为了更好地复用和扩展,如果得到反作用,宁愿不用.
- 有些子类应当覆盖抽象类vitual成员时,千万不能误写为重载.否则很难查出错误.
- 若有两个人做同一个模块时,需要商量好整体架构,明确分工,不可随意修改他人的代码,若要修改也应告诉原作者.若对某些分工意见有分歧时,应慢慢磨合,耐心心细比较,确定可行方案,切不可逃避,将问题延续下去.
- 定义接口时,尽量为使用者(用户)提供明朗,统一,清晰的接口:
1)
尽可能使用默认参数; 2) 使用管理器管理物体的创建删除; 3)
使用抽象类抽象出若干类似Object的接口; 4) 函数名尽量使用动词或动宾结构,并体现出职责; 5)
在实现相同功能的情况下,对外提供的函数应尽可能少; 6)
能声明成private或protected的数据成员和成员函数就不要声明为public.
- 每个对象应当完成并且只完成它该做的事,只管它该管的事,简而言之,就是对象内部,对象之间应该保持"高内聚,低耦合".
- 写一个循环时,如果该循环体比较大,则应该先写好循环判断条件,再实现细节.
while(i > 0) { --i; } //实现如上代码后,再写实现细节(循环体).这样做的目的是可以避免在实现细节后忘记增加--i等这类循环因子更新语句.
如:while(i > 0)
{ 循环
体; --i; }
- 代码中的注释应当致力于解释为什么,而不是怎么做。好的注释并不是重复代码中显而易见的事实,而是引起对代码中微妙的弱点的重
视。明白的代码常常是被注释所玷污了,不过对于作者显而易见的东西对于读者来说常常是晦涩的。一整段的注释要比逐行解释好的多。
- 类的组织Class organization
1).按照以下顺序组织类的定义,按照用户最为关心的顺序组织类的代码: Public type
forward-declarations & typedefs Public
constructors & destructor Public member
functions -----------------------------------------------------------
Protected type forward-declarations & typedefs
Protected member functions
-----------------------------------------------------------
Private type forward-declarations Private
member functions Private data members
2).尽量不要在类的定义体中进行函数定义。模板及内联函数除外。 3).复用public
private
protected关键字,将不同类型的成员分开,如成员函数和数据成员。 4).在继承类里就不要重复写virtual关键字,可以
将它们的声明组织成一组。
- 在使用new/delete,
malloc/free时,注意"谁创建谁销毁"的原则.除非有明确的规约,否则很容易造成内存管理混乱,导致出现内存错误.
- 头文件包含其实是一想很烦琐的工作,不但我们看着累,编译器编译的时候也很累,再加上头文件中常常出现的宏定义。感觉各种宏定
义的展开是非常耗时间的,远不如自定义函数来得速度。我仅就不同头文件、源文件间的句则结构问题提出两点原则,仅供参考:
第一个原则
应该是,如果可以不包含头文件,那就不要包含了。这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没
有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知。 第二个原则应该是,尽量在
CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并便宜成功,那么在A的实现
中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)。
- 解决头文件相互包含问题的方法之一:在.h文件里用class
A声明,数据成员用指针或者引用,在.cpp里用#include"A.h"即可.
- 在索引列表时,最好能根据索引和名字来获取元素,以满足不同场合的需求.在编辑器制作中更彰显其意义.
- 在类A的构造函数里如果有类B成员变量b,并调用了b的某个函数如b.fun(),则b.fun()里边不能调用A的指针.因
为此时A的指针尚未完成空间分配,强行调用将导致出错.
这种情况在Debug下偶尔出错,但在Release版下一定出错.应当引起
重视!
- 以二进制存储信息时,要注意int等类型在不同的平台不同CPU架构下长度是不一样的,故在写入文件时,建议先用宏
(如#define INT_LENGTH
4)来表示写进文件的长度.此外,还要注意字节序(高位在前还是低位在前)的问题.
所以一般情况下,用文本文件来存储相关信息,避免
了那些问题.
- 由于std::vector里边的内存管理机制会适时释放内存以调整合适的大小,故在外部不要保存std::vector里的
元素地址.如果确实要保存,则每次增删vector元素时必须更新外部的指针,否则将可能造成垃圾指针而出现内存错误(有时候甚至不报错,程序运行出现不
可预料的结果.很诡异,很难发现bug).
- 永远不要在类的构造或者析构过程中调用虚函数,因为这样的调用永远不会沿类继承树往下传递到子类中去。否则很有可能出现很隐晦
的bug.参见:http://www.enet.com.cn/article/2005/0706/A20050706431501_2.shtml
- 给有虚函数的模板类添加父类.即将公用接口抽象在父类里,就可以统一处理模板类了.
Feedback
# re: C++编程中的一些感悟 回复 更多评论
2009-04-23 18:22 by
好文!学习了~~
# re: C++编程中的一些感悟 回复 更多评论
2009-04-24 01:31 by
感觉像大杂烩,不过有些条目精辟入里.谢谢!
# re: C++编程中的一些感悟 回复 更多评论
2009-04-24 11:28 by
@Sunshine Alike @sisco 如果本文对大家有所帮助,是我写文章的初衷和动力.谢谢赏阅.
# re: C++编程中的一些感悟 回复 更多评论
2009-08-21 22:43 by
第六条,比较好
|