Making your C++ code robust.docx

上传人:b****3 文档编号:3703254 上传时间:2022-11-24 格式:DOCX 页数:16 大小:71.59KB
下载 相关 举报
Making your C++ code robust.docx_第1页
第1页 / 共16页
Making your C++ code robust.docx_第2页
第2页 / 共16页
Making your C++ code robust.docx_第3页
第3页 / 共16页
Making your C++ code robust.docx_第4页
第4页 / 共16页
Making your C++ code robust.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

Making your C++ code robust.docx

《Making your C++ code robust.docx》由会员分享,可在线阅读,更多相关《Making your C++ code robust.docx(16页珍藏版)》请在冰豆网上搜索。

Making your C++ code robust.docx

MakingyourC++coderobust

MakingyourC++coderobust

∙Introduction

       在实际的项目中,当项目的代码量不断增加的时候,你会发现越来越难管理和跟踪其各个组件,如其不善,很容易就引入BUG。

因此、我们应该掌握一些能让我们程序更加健壮的方法。

       这篇文章提出了一些建议,能有引导我们写出更加强壮的代码,以避免产生灾难性的错误。

即使、因为其复杂性和项目团队结构,你的程序目前不遵循任何编码规则,按照下面列出的简单的规则可以帮助您避免大多数的崩溃情况。

∙Background

       先来介绍下作者开发一些软件(CrashRpt),你可以顾名思义软件崩溃记录软件(库),它能够自动提交你电脑上安装的软件错误记录。

它通过以太网直接将这些错误记录发送给你,这样方便你跟踪软件问题,并及时修改,使得用户感觉到每次发布的软件都有很大的提高,这样他们自然很高兴。

图1、CrashRpt库检测到错误弹出的对话框

      在分析接收的错误记录的时候,我们发现采用下文介绍的方法能够避免大部分程序崩溃的错误。

例如、局部变量未初始化导致数组访问越界,指针使用前未进行检测(NULL)导致访问访问非法区域等。

      我已经总结了几条代码设计的方法和规则,在下文一一列出,希望能够帮助你避免犯一些错误,使得你的程序更加健壮。

∙InitializingLocalVariables 

    使用未初始化的局部变量是引起程序崩溃的一个比较普遍的原因,例如、来看下面这段程序片段:

viewplain

1.// Define local variables  

2.BOOL bExitResult; // This will be TRUE if the function exits successfully  

3.FILE* f; // Handle to file  

4.TCHAR szBuffer[_MAX_PATH];   // String buffer  

5.    

6.// Do something with variables above...   

    上面的这段代码存在着一个潜在的错误,因为没有一个局部变量初始化了。

当你的代码运行的时候,这些变量将被默认负一些错误的数值。

例如bExitResult数值将被负为-135913245,szBuffer 必须以“\0”结尾,结果不会。

因此、局部变量初始化时非常重要的,如下正确代码:

viewplain

1.// Define local variables  

2.  

3.// Initialize function exit code with FALSE to indicate failure assumption  

4.BOOL bExitResult = FALSE; // This will be TRUE if the function exits successfully  

5.// Initialize file handle with NULL  

6.FILE* f = NULL; // Handle to file  

7.// Initialize string buffer with empty string  

8.TCHAR szBuffer[_MAX_PATH] = _T("");   // String buffer  

9.// Do something with variables above...   

    注意:

有人说变量初始化会引起程序效率降低,是的,确实如此,如果你确实非常在乎程序的执行效率,去除局部变量初始化,你得想好其后果。

∙InitializingWinAPIStructures

      许多WindowsAPI都接受或则返回一些结构体参数,结构体如果没有正确的初始化,也很有可能引起程序崩溃。

大家可能会想起用ZeroMemory宏或者memset()函数去用0填充这个结构体(对结构体对应的元素设置默认值)。

但是大部分WindowsAPI结构体都必须有一个cbSIze参数,这个参数必须设置为这个结构体的大小。

      看看下面代码,如何初始化WindowsAPI结构体参数:

viewplain

1.NOTIFYICONDATA nf; // WinAPI structure  

2.memset(&nf,0,sizeof(NOTIFYICONDATA)); // Zero memory  

3.nf.cbSize = sizeof(NOTIFYICONDATA); // Set structure size!

  

4.// Initialize other structure members  

5.nf.hWnd = hWndParent;  

6.nf.uID = 0;     

7.nf.uFlags = NIF_ICON | NIF_TIP;  

8.nf.hIcon = :

:

LoadIcon(NULL, IDI_APPLICATION);  

9._tcscpy_s(nf.szTip, 128, _T("Popup Tip Text"));  

10.        

11.// Add a tray icon  

12.Shell_NotifyIcon(NIM_ADD, &nf);  

     注意:

千万不要用ZeroMemory和memset去初始化那些包括结构体对象的结构体,这样很容易破坏其内部结构体,从而导致程序崩溃.

viewplain

1.// Declare a C++ structure  

2.struct ItemInfo  

3.{  

4.  std:

:

string sItemName; // The structure has std:

:

string object inside  

5.  int nItemValue;  

6.};   

7.  

8.// Init the structure  

9.ItemInfo item;  

10.// Do not use memset()!

 It can corrupt the structure  

11.// memset(&item, 0, sizeof(ItemInfo));  

12.// Instead use the following  

13.item.sItemName = "item1";  

14.item.nItemValue = 0;   

15.   这里最好是用结构体的构造函数对其成员进行初始化.  

16.  

17.// Declare a C++ structure  

18.struct ItemInfo  

19.{  

20.  // Use structure constructor to set members with default values  

21.  ItemInfo()  

22.  {  

23.    sItemName = _T("unknown");  

24.    nItemValue = -1;  

25.  }  

26.        

27.  std:

:

string sItemName; // The structure has std:

:

string object inside  

28.  int nItemValue;  

29.};  

30.// Init the structure  

31.ItemInfo item;  

32.// Do not use memset()!

 It can corrupt the structure  

33.// memset(&item, 0, sizeof(ItemInfo));  

34.// Instead use the following  

35.item.sItemName = "item1";  

36.item.nItemValue = 0;      

∙ValidatingFunctionInput 

     在函数设计的时候,对传入的参数进行检测是一直都推荐的。

例如、如果你设计的函数是公共API的一部分,它可能被外部客户端调用,这样很难保证客户端传进入的参数就是正确的。

     例如,让我们来看看这个hypotethicalDrawVehicle() 函数,它可以根据不同的质量来绘制一辆跑车,这个质量数值(nDrawingQaulity)是0~100。

prcDraw 定义这辆跑车的轮廓区域。

     看看下面代码,注意观察我们是如何在使用函数参数之前进行参数检测:

viewplain

1.BOOL DrawVehicle(HWND hWnd, LPRECT prcDraw, int nDrawingQuality)  

2.  {  

3.    // Check that window is valid  

4.    if(!

IsWindow(hWnd))  

5.      return FALSE;  

6.   

7.    // Check that drawing rect is valid  

8.    if(prcDraw==NULL)  

9.      return FALSE;  

10.   

11.    // Check drawing quality is valid  

12.    if(nDrawingQuality<0 || nDrawingQuality>100)  

13.      return FALSE;  

14.     

15.    // Now it's safe to draw the vehicle  

16.   

17.    // ...  

18.   

19.    return TRUE;  

20.  }  

∙ValidatingPointers

      在指针使用之前,不检测是非常普遍的,这个可以说是我们引起软件崩溃最有可能的原因。

如果你用一个指针,这个指针刚好是NULL,那么你的程序在运行时,将报出异常。

viewplain

1.CVehicle* pVehicle = GetCurrentVehicle();  

2.  

3.// Validate pointer  

4.if(pVehicle==NULL)  

5.{  

6.  // Invalid pointer, do not use it!

  

7.  return FALSE;  

8.}  

∙InitializingFunctionOutput

    如果你的函数创建了一个对象,并要将它作为函数的返回参数。

那么记得在使用之前把他复制为NULL。

如不然,这个函数的调用者将使用这个无效的指针,进而一起程序错误。

如下错误代码:

viewplain

1.int CreateVehicle(CVehicle** ppVehicle)  

2.  {  

3.    if(CanCreateVehicle())  

4.    {  

5.      *ppVehicle = new CVehicle();  

6.      return 1;  

7.    }      

8.   

9.    // If CanCreateVehicle() returns FALSE,  

10.    // the pointer to *ppVehcile would never be set!

  

11.    return 0;  

12.  }  

13.  

14.      正确的代码如下;  

15.  

16.  int CreateVehicle(CVehicle** ppVehicle)  

17.  {  

18.    // First initialize the output parameter with NULL  

19.    *ppVehicle = NULL;  

20.   

21.    if(CanCreateVehicle())  

22.    {  

23.      *ppVehicle = new CVehicle();  

24.      return 1;  

25.    }      

26.   

27.    return 0;  

28.  }  

∙CleaningUpPointerstoDeletedObjects

    在内存释放之后,无比将指针复制为NULL。

这样可以确保程序的没有那个地方会再使用无效指针。

其实就是,访问一个已经被删除的对象地址,将引起程序异常。

如下代码展示如何清除一个指针指向的对象:

viewplain

1.// Create object  

2.CVehicle* pVehicle = new CVehicle();  

3.delete pVehicle; // Free pointer  

4.pVehicle = NULL; // Set pointer with NULL  

∙CleaningUpReleasedHandles 

     在释放一个句柄之前,务必将这个句柄复制伪NULL(0或则其他默认值)。

这样能够保证程序其他地方不会重复使用无效句柄。

看看如下代码,如何清除一个WindowsAPI的文件句柄:

viewplain

1.HANDLE hFile = INVALID_HANDLE_VALUE;   

2.  

3.// Open file  

4.hFile = CreateFile(_T("example.dat"), FILE_READ|FILE_WRITE, FILE_OPEN_EXISTING);  

5.if(hFile==INVALID_HANDLE_VALUE)  

6.{  

7.  return FALSE; // Error opening file  

8.}  

9.  

10.// Do something with file  

11.  

12.// Finally, close the handle  

13.if(hFile!

=INVALID_HANDLE_VALUE)  

14.{  

15.  CloseHandle(hFile);   // Close handle to file  

16.  hFile = INVALID_HANDLE_VALUE;   // Clean up handle  

17.}   

    下面代码展示如何清除File*句柄:

viewplain

1.// First init file handle pointer with NULL  

2.FILE* f = NULL;  

3.  

4.// Open handle to file  

5.errno_t err = _tfopen_s(_T("example.dat"), _T("rb"));  

6.if(err!

=0 || f==NULL)  

7.  return FALSE; // Error opening file  

8.  

9.// Do something with file  

10.  

11.// When finished, close the handle  

12.if(f!

=NULL) // Check that handle is valid  

13.{  

14.  fclose(f);  

15.  f = NULL; // Clean up pointer to handle  

16.}   

∙Usingdelete[]OperatorforArrays 

    如果你分配一个单独的对象,可以直接使用new ,同样你释放单个对象的时候,可以直接使用delete.然而,申请一个对象数组对象的时候可以使用new,但是释放的时候就不能使用delete,而必须使用delete[]:

viewplain

1. // Create an array of objects  

2. CVehicle* paVehicles = new CVehicle[10];  

3. delete [] paVehicles; // Free pointer to array  

4. paVehicles = NULL; // Set pointer with NULL  

5.or  

6. // Create a buffer of bytes  

7. LPBYTE pBuffer = new BYTE[255];  

8. delete [] pBuffer; // Free pointer to array  

9. pBuffer = NULL; // Set pointer with NULL  

∙AllocatingMemoryCarefully 

    有时候,程序需要动态分配一段缓冲区,这个缓冲区是在程序运行的时候决定的。

例如、你需要读取一个文件的内容,那么你就需要申请该文件大小的缓冲区来保存该文件的内容。

在申请这段内存之前,请注意,malloc()ornew是不能申请0字节的内存,如不然,将导致malloc()ornew函数调用失败。

传递错误的参数给malloc()函数将导致C运行时错误。

如下代码展示如何动态申请内存:

viewplain

1.// Determine what buffer to allocate.  

2.UINT uBufferSize = GetBufferSize();   

3.  

4.LPBYTE* pBuffer = NULL; // Init pointer to buffer  

5.  

6.// Allocate a buffer only if buffer size > 0  

7.if(uBufferSize>0)  

8. pBuffer = new BYTE[uBufferSize];  

     为了进一步了解如何正确的分配内存,你可以读下SecureCodingBestPracticesforMemoryAllocationinCandC++这篇文章。

∙UsingAssertsCarefully

      Asserts用语调试模式检测先决条件和后置条件。

但当我们编译器处于release模式的时候,Asserts在预编阶段被移除。

因此,用Asserts是不能够检测我们的程序状态,错误代码如下:

viewplain

1.#include   

2.   

3. // This function reads a sports car's model from a file  

4. CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName)  

5. {  

6.   CVehicle* pVehicle = NULL; // Pointer to vehicle object  

7.  

8.   // Check preconditions  

9.   assert(szFileName!

=NULL); // This will be removed by preprocessor in Release mode!

  

10.   assert(_tcslen(szFileName)!

=0); // This will be removed in Release mode!

  

11.  

12.   // Open the file  

13.   FILE* f = _tfopen(szFileName, _T("rt"));  

14.  

15.   // Create new CVehicle object  

16.   pVehicle = new CVehicle();  

17.  

18.   // Read vehicle model from file  

19.  

20.   // Check postcondition   

21.   assert(pVehicle->GetWheelCount()==4); // This will be removed in Release mode!

  

22.  

23.   // Return pointer to the vehicle object  

24.   return pVehicle;  

25. }  

     看看上述的代码,Asserts能够在debug模式下检测我们的程序,在release模式下却不能。

所以我们还是不得不用if()来这步检测操作。

正确的代码如下;

viewplain

1.CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName, )  

2. {  

3.   CVehicle* pVehicle = NULL; // Pointer to vehicle object  

4.  

5.   // Check preconditions  

6.   assert(szFileName!

=NULL); // This will be removed by preprocessor in Release mode!

  

7.   assert(_tcslen(szFileName)!

=0); // This will be removed in Release mode!

  

8.  

9.   if(szFileName==NULL || _tcslen(szFileName)==0)  

10.     return NULL; // Invalid input parameter  

11.  

12.   // Open the file  

13.   FILE* f = _tfopen(szFileName, _T("rt"));  

14.  

15.   // Create new CVehicle object  

16.   pVeh

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1