十年编程经验分享

 经验分享,如何写线程 

写线程我想每人不会吧,我只是谈谈我自己的经验 
首先一定不要使用beginthread,其次最好也不使用CreateThread,看MSDN讲最好用_beginthread或者_beginthreadex 
写线程函数,我个人的经验是把线程函数写在类里,而且写出protected成员函数,而不是写成一个单独的函数,原因是: 
1。线程函数一般都是和类有关系 
2。写成类的函数,这样在线程函数里就可以调用类的成员变量。如果写成一个单独的函数,就不能直接调用类的私有成员变量了 
3。写成protected成员函数,可以供继承类调用 

另外我想大家写线程的参数传递方法都差不多,就是把类的指针传进去 
有时候我们需要传递其他的值,这时候我习惯是在cpp文件中定义一个全局的类指针,在线程函数里直接使用



经验分享,如何写DLL 

DLL一般有2种写法。 
第一种用MS.NET的向导帮助就能生产一个DLL,很简单。也就是有个类的头文件,写好DLL后把DLL和头文件给调用端一起使用。 
第二种是写一个接口文件。 

在这里,我介绍一下通过接口文件写DLL。接口文件主要就是用到纯虚类。(我以前明白什么是纯虚类,但就是不知道怎么用。也看过关于接口的介绍,但没有看到实际的例子) 
MSDN关于DLL也提到一句话,就是通过接口来写,但具体怎么写就没说了。 

我们需要一个接口文件(h文件),一个实现接口的类的文件(h和cpp文件或者只有h文件) 
接口文件名一般用“I”开头,实现接口的类的文件一般用Impl结尾。例如: 

//---------------------接口文件 IMyDLL.h ------ 

 


这样基本上就写完了。调用端使用的时候只要DLL的头文件(MyDLL.h),库文件和接口文件(IMyDLL.h) 


为什么要使用接口写DLL,一个原因也就是MSDN说的在某些时候如果你给调用端一个类的头文件,可能这个头文件需要include很多其他的文件,对调用端并不合适。 
我自己也碰到过类似的问题,调用端不能编译,后来改成接口文件就好了。 
使用接口文件,就没有其他的什么文件。另外一个原因就是接口设计自身的好处了,这里就不说了。 


还要记得在接口文件中定义一个Release函数。供调用端释放内存 
Release函数很简单,就是写 
delete this;



经验分享,如何使用try,catch, throw之一 


try,catch, throw的含义就很清楚,但怎么使用,我还是过了好多年才明白一些 

先看一个不好的例子 

       
         
//----------------- 不好的代码 -----------------

下面是我个人比较喜欢的写法 

       
         
//--------- 好的例子 ---------------

简单说一下 
第一种写法,需要在不同的地方delete 不同的变量 
第二种写法,在catch里delete所有的变量,代码的结构看起来更容易读,也易于维护 

//define 
  CTest1 * pTest1 = NULL; 
  CTest2 * pTest2 = NULL; 
  CTest3 * pTest3 = NULL; 
变量定义一定要在try之前定义,否则catch里找不到这些变量的定义 

另外很重要的一点,在delete之后必须将变量设成NULL 

catch里的delete可以删掉 
把try里的delete放catch后,因为指针都初始化NULL了 



经验分享,如何使用try,catch, throw之二 

什么时候使用try,catch,什么时候不用;什么时候用throw,什么时候不用。工作了很多年才明白。 

我个人的理解是: 
1。在private或者protected的成员函数不使用try,catch,而只使用throw 
2。如果在private或者protected的成员函数需要使用try,catch,那么就要使用rethrow 
3。在public成员函数里使用try,catch 
4。如果该类相对于整个项目来说是属于被调用层,那么public成员函数也可以不使用try,catch 
5。如果调用第三方的代码,我一般都会用try,catch 

我个人的习惯是把private或者protected成员函数的名字使用前缀__,public函数不用 

先看一个我不喜欢的写法 

       
         
//------------- try, catch, throw 例子,不喜欢的写法 ------------

我喜欢的写法是private或者protected函数没有try,catch,只有throw 
在public函数就不需要去判断每个private或者protected函数的返回值了 
看起来会更清楚一点 

如果在private或者protected的成员函数需要使用try,catch,那么就要再throw,而不是调用return 

回想一下windows的win32函数,返回有2种方式 
一种是返回一个错误值 
另外一种就是抛出异常 
在我自己的设计中,什么时候返回值什么时候抛出异常,很值得推敲一下 


另外,仔细考虑一下private和protected函数,它们永远都会返回1或者在它们出错的时候就会throw,那在写函数声明的时候也就不需要返回值了,就返回void就可以了,也就不需要写return语句了。

然后,在某些情况下,有时候我们真的需要private或者protected函数返回错误代码,而不是throw,这个时候如果再声明函数的时候有返回值,就可以和没有返回值的函数区别开了。



经验分享,代码编写习惯 

以下是我个人的习惯: 
1。将所有的错误代码返回值写在一个文件里,使用define来定义错误代码 
2。写几const来定义操作错误代码,比如: 
typedef int MY_ERR; 
const int  MY_UNKNOW_ERR 0 
const int  MY_OK 1 
#define MY_SUCCEEDED(Status) ((MY_ERR)(Status) == MY_OK) 
#define MY_FAILED(Status) ((MY_ERR)(Status) != MY_OK) 
3。函数的返回值基本上都是MY_ERR,或者是void,很少有BOOL。因为很多情况下我们需要知道函数错在哪里,错误代码是什么。 
函数返回值是MY_ERR,而不是int。这样表示函数返回的是一个错误代码。如果函数返回int,那就表示函数的确是返回一个int的数值 
4。如何写类函数,请参考我的帖子"经验分享,如何使用try,catch, throw之二" 
5。基本上调用public函数的写法是: 
  MY_ERR err = myclass.test1(); 
  if MY_FAILED(err) 
      throw err; 
6。写一个错误消息映射文件,即每个错误代码映射一窜字符。字符可以是多语言的。一般写在xml文件里。这样根据每个函数的返回值,就可以得到映射的字符串。 
7。另外会有一个专门写日志的文件,日志文件有两种,一个是给用户看的操作日志,一个是给自己看的调试日志 
8。尽量不用在底层的代码里写MessageBox这样的函数。一个项目在哪里弹出对话框,大多少情况下都是由GUI来决定的,不是由底层的代码来决定的。 
9。提供一个common或者叫public的类,提供所有项目都可以使用的函数或者类 
10。另外就是总所周知的事情,使用N层系统去做代码设计 

日志有两种,一种是操作日志,一种是调试日志 
操作日志是记录用户每一步的操作情况,也不一定要写的很详细,也不一定要记录每一步 
调试日志是你认为需要记录的内容,比如一些变量的值。 
调试日志你可以写个工具实时查看,这样有一个好处,可以实时调试程序, 
在一些情况下,我们不能用.NET的调试工具,我们需要实时运行程序,不能用断点中断调试,这时候用调试日志就很有用。 
比如在调试线程的时候,有时候使用断点是不能得到正确结果的。 
我个人的习惯是调试信息写成“【类名】【方法名】调试信息” 

另外对于错误的信息,不仅要写到日志里,还要考虑是否弹出对话框给用户 

错误代码的使用 

先看一个不好的例子 

 

提供一个函数,通过error code得到错误信息 

我在写下位机程序,并且下位机内存有限,个人认为要尽量使用#define,不知道对不对。 

我在写下位机程序时就碰到内存不够的问题,将const改成#define就好了 

另外在使用enum定义错误代码的时候,有时候需要从enum转换到int,自己感觉有点麻烦 
所以自己也就使用宏定义了,不过是一定要小心使用


线程函数写成类的protected函数

如果这个函数不是属于类的,那么就不能访问类的私有成员变量 
现在的前提是这个函数是类的函数,它就可以访问类的所有成员变量 

换一个角度,你先设计好了一个类,后来发现这个类的其中一个方法在线程下运行更合适,这个时候就把这个函数设计成线程调用的函数 
并且,这个函数仍然是类的一个成员函数,你不能因为它在线程下运行更合适,就更改你的类的设计 

线程是操作系统的概念,不是类的概念



经验分享,C语言代码编写习惯 

C++可以利用try, catch, throw来写代码风格 
C语言没有, 我个人使用goto语句来写代码风格 

大家都知道最好不要使用goto, 但不是完全不要使用goto语句 
函数设计有一个原则, 就是一个函数只有一个出口, 也就是只有一个地方有return 

Code

经验分享,串口的通讯结构代码 


我在工作中写过串口调用的代码,我想我写的代码应该还算是比较规范,可以给大家做一个参考 
在代码中会用到一个叫SAMA.dll的文件, 关于它的源代码可以去下载,下载地址是 
http://d.download.csdn.net/down/1150752/zyx040404 

我写的这个串口封装调用是用在探针台的自动上下片系统里, 串口的封装代码在SAMA里, 此串口的通讯代码的结构是: 
ILoader.h接口文件, 定义了上位机和下位机发送命令的函数等等 
LoaderImpl.h, LoaderImpl.cpp 继承了ILoader.h文件, 是实现体 
LoaderMCUDataDefine.h定义了上下位机共同使用的数据结构和命令格式代码以及错误代码,上下位机的命令格式是第一个字节是0xFF,第二和第三个是重复的命令字节,这样可以避免收到错误的串口数据 
LoaderDataDefine.h定义了上位机的一些数据结构和消息定义,这些消息将发送给主窗口,由主窗口做相应的处理 

我现在写上部分代码 

       
         
Code
//LoaderImpl.h
//LoaderImpl.cpp
//接着LoaderImpl.cpp
//LoaderMCUDataDefine.h
//LoaderDataDefine.h
//还有一个是LoaderErrorCode.h
该项目会生成一个dll文件,供主程序去调用 
有关写日志的代码请参考SAMA,在我的资源里有下载

解释一下 
SendCommandToLowMachine 
SendCommandToLowMachineEx是发送命令并且带一个参数 
__SendCommandToLowMachineOnForce是直接发送,不需要检查是否可以发送的状态,上面两个需要检查 
参数dwWaitTime是等待下位机回复的时间 
参数bIsForcedSend是否不需要检测状态直接发送 
参数bWaitResult是否需要等待下位机一定要回复信息,这里会有一个事件对象 
UpdateLowMachinePara是发送多个参数给下位机 

__OnReceive需要仔细看看,前面一段是判断是不是我们规定的命令格式,中间是命令的处理 
如果需要通知主窗口,还会发消息给主窗口

注意m_bIsWorking的付值时间和条件,其实目前的写法有错误

 


 

你可能感兴趣的:(编程)