Making your C++ code robust

本文探讨了在C++编程中实现更强大、更稳定的代码的方法,旨在减少程序崩溃的风险。通过实例分析了局部变量初始化、Windows API结构体的正确初始化、函数输入验证、指针有效性检查、函数输出清理、内存管理、断言的恰当使用、返回代码检查、智能指针的运用、等号与等于操作符的区别,以及避免常见编程错误的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Making your C++ code robust
  • Introduction

在实际的项目中,当项目的代码量不断增加的时候,你会发现越来越难管理和跟踪其各个组件,如其不善,很容易就引入BUG。因此、我们应该掌握一些能让我们程序更加健壮的方法。

这篇文章提出了一些建议,能有引导我们写出更加强壮的代码,以避免产生灾难性的错误。即使、因为其复杂性和项目团队结构,你的程序目前不遵循任何编码规则,按照下面列出的简单的规则可以帮助您避免大多数的崩溃情况。

  • Background

先来介绍下作者开发一些软件(CrashRpt),你可以http://code.google.com/p/crashrpt/网站上下载源代码。CrashRpt 顾名思义软件崩溃记录软件(库),它能够自动提交你电脑上安装的软件错误记录。它通过以太网直接将这些错误记录发送给你,这样方便你跟踪软件问题,并及时修改,使得用户感觉到每次发布的软件都有很大的提高,这样他们自然很高兴。

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

在分析接收的错误记录的时候,我们发现采用下文介绍的方法能够避免大部分程序崩溃的错误。例如、局部变量未初始化导致数组访问越界,指针使用前未进行检测(NULL)导致访问访问非法区域等。

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

  • Initializing Local Variables

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

  1. //Definelocalvariables
  2. BOOLbExitResult;//ThiswillbeTRUEifthefunctionexitssuccessfully
  3. FILE*f;//Handletofile
  4. TCHARszBuffer[_MAX_PATH];//Stringbuffer
  5. //Dosomethingwithvariablesabove...
// Define local variables BOOL bExitResult; // This will be TRUE if the function exits successfully FILE* f; // Handle to file TCHAR szBuffer[_MAX_PATH]; // String buffer // Do something with variables above...

上面的这段代码存在着一个潜在的错误,因为没有一个局部变量初始化了。当你的代码运行的时候,这些变量将被默认负一些错误的数值。例如bExitResult 数值将被负为-135913245 ,szBuffer必须以“\0”结尾,结果不会。因此、局部变量初始化时非常重要的,如下正确代码:

  1. //Definelocalvariables
  2. //InitializefunctionexitcodewithFALSEtoindicatefailureassumption
  3. BOOLbExitResult=FALSE;//ThiswillbeTRUEifthefunctionexitssuccessfully
  4. //InitializefilehandlewithNULL
  5. FILE*f=NULL;//Handletofile
  6. //Initializestringbufferwithemptystring
  7. TCHARszBuffer[_MAX_PATH]=_T("");//Stringbuffer
  8. //Dosomethingwithvariablesabove...
// Define local variables // Initialize function exit code with FALSE to indicate failure assumption BOOL bExitResult = FALSE; // This will be TRUE if the function exits successfully // Initialize file handle with NULL FILE* f = NULL; // Handle to file // Initialize string buffer with empty string TCHAR szBuffer[_MAX_PATH] = _T(""); // String buffer // Do something with variables above...

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

  • Initializing WinAPI Structures

许多Windows API都接受或则返回一些结构体参数,结构体如果没有正确的初始化,也很有可能引起程序崩溃。大家可能会想起用ZeroMemory宏或者memset()函数去用0填充这个结构体(对结构体对应的元素设置默认值)。但是大部分Windows API 结构体都必须有一个cbSIze参数,这个参数必须设置为这个结构体的大小。

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

  1. NOTIFYICONDATAnf;//WinAPIstructure
  2. memset(&nf,0,sizeof(NOTIFYICONDATA));//Zeromemory
  3. nf.cbSize=sizeof(NOTIFYICONDATA);//Setstructuresize!
  4. //Initializeotherstructuremembers
  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("PopupTipText"));
  10. //Addatrayicon
  11. Shell_NotifyIcon(NIM_ADD,&nf);
NOTIFYICONDATA nf; // WinAPI structure memset(&nf,0,sizeof(NOTIFYICONDATA)); // Zero memory nf.cbSize = sizeof(NOTIFYICONDATA); // Set structure size! // Initialize other structure members nf.hWnd = hWndParent; nf.uID = 0; nf.uFlags = NIF_ICON | NIF_TIP; nf.hIcon = ::LoadIcon(NULL, IDI_APPLICATION); _tcscpy_s(nf.szTip, 128, _T("Popup Tip Text")); // Add a tray icon Shell_NotifyIcon(NIM_ADD, &nf);

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

  1. //DeclareaC++structure
  2. structItemInfo
  3. {
  4. std::stringsItemName;//Thestructurehasstd::stringobjectinside
  5. intnItemValue;
  6. };
  7. //Initthestructure
  8. ItemInfoitem;
  9. //Donotusememset()!Itcancorruptthestructure
  10. //memset(&item,0,sizeof(ItemInfo));
  11. //Insteadusethefollowing
  12. item.sItemName="item1";
  13. item.nItemValue=0;
  14. 这里最好是用结构体的构造函数对其成员进行初始化.
  15. //DeclareaC++structure
  16. structItemInfo
  17. {
  18. //Usestructureconstructortosetmemberswithdefaultvalues
  19. ItemInfo()
  20. {
  21. sItemName=_T("unknown");
  22. nItemValue=-1;
  23. }
  24. std::stringsItemName;//Thestructurehasstd::stringobjectinside
  25. intnItemValue;
  26. };
  27. //Initthestructure
  28. ItemInfoitem;
  29. //Donotusememset()!Itcancorruptthestructure
  30. //memset(&item,0,sizeof(ItemInfo));
  31. //Insteadusethefollowing
  32. item.sItemName="item1";
  33. item.nItemValue=0;
// Declare a C++ structure struct ItemInfo { std::string sItemName; // The structure has std::string object inside int nItemValue; }; // Init the structure ItemInfo item; // Do not use memset()! It can corrupt the structure // memset(&item, 0, sizeof(ItemInfo)); // Instead use the following item.sItemName = "item1"; item.nItemValue = 0; 这里最好是用结构体的构造函数对其成员进行初始化. // Declare a C++ structure struct ItemInfo { // Use structure constructor to set members with default values ItemInfo() { sItemName = _T("unknown"); nItemValue = -1; } std::string sItemName; // The structure has std::string object inside int nItemValue; }; // Init the structure ItemInfo item; // Do not use memset()! It can corrupt the structure // memset(&item, 0, sizeof(ItemInfo)); // Instead use the following item.sItemName = "item1"; item.nItemValue = 0;
  • Validating Function Input

在函数设计的时候,对传入的参数进行检测是一直都推荐的。例如、如果你设计的函数是公共API的一部分,它可能被外部客户端调用,这样很难保证客户端传进入的参数就是正确的。

例如,让我们来看看这个hypotethical DrawVehicle()函数,它可以根据不同的质量来绘制一辆跑车,这个质量数值(nDrawingQaulity )是0~100。prcDraw定义这辆跑车的轮廓区域。

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

  1. BOOLDrawVehicle(HWNDhWnd,LPRECTprcDraw,intnDrawingQuality)
  2. {
  3. //Checkthatwindowisvalid
  4. if(!IsWindow(hWnd))
  5. returnFALSE;
  6. //Checkthatdrawingrectisvalid
  7. if(prcDraw==NULL)
  8. returnFALSE;
  9. //Checkdrawingqualityisvalid
  10. if(nDrawingQuality<0||nDrawingQuality>100)
  11. returnFALSE;
  12. //Nowit'ssafetodrawthevehicle
  13. //...
  14. returnTRUE;
  15. }
BOOL DrawVehicle(HWND hWnd, LPRECT prcDraw, int nDrawingQuality) { // Check that window is valid if(!IsWindow(hWnd)) return FALSE; // Check that drawing rect is valid if(prcDraw==NULL) return FALSE; // Check drawing quality is valid if(nDrawingQuality<0 || nDrawingQuality>100) return FALSE; // Now it's safe to draw the vehicle // ... return TRUE; }
  • Validating Pointers

在指针使用之前,不检测是非常普遍的,这个可以说是我们引起软件崩溃最有可能的原因。如果你用一个指针,这个指针刚好是NULL,那么你的程序在运行时,将报出异常。

  1. CVehicle*pVehicle=GetCurrentVehicle();
  2. //Validatepointer
  3. if(pVehicle==NULL)
  4. {
  5. //Invalidpointer,donotuseit!
  6. returnFALSE;
  7. }
CVehicle* pVehicle = GetCurrentVehicle(); // Validate pointer if(pVehicle==NULL) { // Invalid pointer, do not use it! return FALSE; }
  • Initializing Function Output

如果你的函数创建了一个对象,并要将它作为函数的返回参数。那么记得在使用之前把他复制为NULL。如不然,这个函数的调用者将使用这个无效的指针,进而一起程序错误。如下错误代码:

  1. intCreateVehicle(CVehicle**ppVehicle)
  2. {
  3. if(CanCreateVehicle())
  4. {
  5. *ppVehicle=newCVehicle();
  6. return1;
  7. }
  8. //IfCanCreateVehicle()returnsFALSE,
  9. //thepointerto*ppVehcilewouldneverbeset!
  10. return0;
  11. }
  12. 正确的代码如下;
  13. intCreateVehicle(CVehicle**ppVehicle)
  14. {
  15. //FirstinitializetheoutputparameterwithNULL
  16. *ppVehicle=NULL;
  17. if(CanCreateVehicle())
  18. {
  19. *ppVehicle=newCVehicle();
  20. return1;
  21. }
  22. return0;
  23. }
int CreateVehicle(CVehicle** ppVehicle) { if(CanCreateVehicle()) { *ppVehicle = new CVehicle(); return 1; } // If CanCreateVehicle() returns FALSE, // the pointer to *ppVehcile would never be set! return 0; } 正确的代码如下; int CreateVehicle(CVehicle** ppVehicle) { // First initialize the output parameter with NULL *ppVehicle = NULL; if(CanCreateVehicle()) { *ppVehicle = new CVehicle(); return 1; } return 0; }
  • Cleaning Up Pointers to Deleted Objects

在内存释放之后,无比将指针复制为NULL。这样可以确保程序的没有那个地方会再使用无效指针。其实就是,访问一个已经被删除的对象地址,将引起程序异常。如下代码展示如何清除一个指针指向的对象:

  1. //Createobject
  2. CVehicle*pVehicle=newCVehicle();
  3. deletepVehicle;//Freepointer
  4. pVehicle=NULL;//SetpointerwithNULL
// Create object CVehicle* pVehicle = new CVehicle(); delete pVehicle; // Free pointer pVehicle = NULL; // Set pointer with NULL
  • Cleaning Up Released Handles

在释放一个句柄之前,务必将这个句柄复制伪NULL (0或则其他默认值)。这样能够保证程序其他地方不会重复使用无效句柄。看看如下代码,如何清除一个Windows API的文件句柄:

  1. HANDLEhFile=INVALID_HANDLE_VALUE;
  2. //Openfile
  3. hFile=CreateFile(_T("example.dat"),FILE_READ|FILE_WRITE,FILE_OPEN_EXISTING);
  4. if(hFile==INVALID_HANDLE_VALUE)
  5. {
  6. returnFALSE;//Erroropeningfile
  7. }
  8. //Dosomethingwithfile
  9. //Finally,closethehandle
  10. if(hFile!=INVALID_HANDLE_VALUE)
  11. {
  12. CloseHandle(hFile);//Closehandletofile
  13. hFile=INVALID_HANDLE_VALUE;//Cleanuphandle
  14. }
HANDLE hFile = INVALID_HANDLE_VALUE; // Open file hFile = CreateFile(_T("example.dat"), FILE_READ|FILE_WRITE, FILE_OPEN_EXISTING); if(hFile==INVALID_HANDLE_VALUE) { return FALSE; // Error opening file } // Do something with file // Finally, close the handle if(hFile!=INVALID_HANDLE_VALUE) { CloseHandle(hFile); // Close handle to file hFile = INVALID_HANDLE_VALUE; // Clean up handle }

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

  1. //FirstinitfilehandlepointerwithNULL
  2. FILE*f=NULL;
  3. //Openhandletofile
  4. errno_terr=_tfopen_s(_T("example.dat"),_T("rb"));
  5. if(err!=0||f==NULL)
  6. returnFALSE;//Erroropeningfile
  7. //Dosomethingwithfile
  8. //Whenfinished,closethehandle
  9. if(f!=NULL)//Checkthathandleisvalid
  10. {
  11. fclose(f);
  12. f=NULL;//Cleanuppointertohandle
  13. }
// First init file handle pointer with NULL FILE* f = NULL; // Open handle to file errno_t err = _tfopen_s(_T("example.dat"), _T("rb")); if(err!=0 || f==NULL) return FALSE; // Error opening file // Do something with file // When finished, close the handle if(f!=NULL) // Check that handle is valid { fclose(f); f = NULL; // Clean up pointer to handle }
  • Using delete [] Operator for Arrays

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

  1. //Createanarrayofobjects
  2. CVehicle*paVehicles=newCVehicle[10];
  3. delete[]paVehicles;//Freepointertoarray
  4. paVehicles=NULL;//SetpointerwithNULL
  5. or
  6. //Createabufferofbytes
  7. LPBYTEpBuffer=newBYTE[255];
  8. delete[]pBuffer;//Freepointertoarray
  9. pBuffer=NULL;//SetpointerwithNULL
// Create an array of objects CVehicle* paVehicles = new CVehicle[10]; delete [] paVehicles; // Free pointer to array paVehicles = NULL; // Set pointer with NULLor // Create a buffer of bytes LPBYTE pBuffer = new BYTE[255]; delete [] pBuffer; // Free pointer to array pBuffer = NULL; // Set pointer with NULL
  • Allocating Memory Carefully

有时候,程序需要动态分配一段缓冲区,这个缓冲区是在程序运行的时候决定的。例如、你需要读取一个文件的内容,那么你就需要申请该文件大小的缓冲区来保存该文件的内容。在申请这段内存之前,请注意,malloc() or new是不能申请0字节的内存,如不然,将导致malloc() or new函数调用失败。传递错误的参数给malloc() 函数将导致C运行时错误。如下代码展示如何动态申请内存:

  1. //Determinewhatbuffertoallocate.
  2. UINTuBufferSize=GetBufferSize();
  3. LPBYTE*pBuffer=NULL;//Initpointertobuffer
  4. //Allocateabufferonlyifbuffersize>0
  5. if(uBufferSize>0)
  6. pBuffer=newBYTE[uBufferSize];
// Determine what buffer to allocate. UINT uBufferSize = GetBufferSize(); LPBYTE* pBuffer = NULL; // Init pointer to buffer // Allocate a buffer only if buffer size > 0 if(uBufferSize>0) pBuffer = new BYTE[uBufferSize];

为了进一步了解如何正确的分配内存,你可以读下Secure Coding Best Practices for Memory Allocation in C and C++这篇文章。

  • Using Asserts Carefully

Asserts用语调试模式检测先决条件和后置条件。但当我们编译器处于release模式的时候,Asserts在预编阶段被移除。因此,用Asserts是不能够检测我们的程序状态,错误代码如下:

  1. #include<assert.h>
  2. //Thisfunctionreadsasportscar'smodelfromafile
  3. CVehicle*ReadVehicleModelFromFile(LPCTSTRszFileName)
  4. {
  5. CVehicle*pVehicle=NULL;//Pointertovehicleobject
  6. //Checkpreconditions
  7. assert(szFileName!=NULL);//ThiswillberemovedbypreprocessorinReleasemode!
  8. assert(_tcslen(szFileName)!=0);//ThiswillberemovedinReleasemode!
  9. //Openthefile
  10. FILE*f=_tfopen(szFileName,_T("rt"));
  11. //CreatenewCVehicleobject
  12. pVehicle=newCVehicle();
  13. //Readvehiclemodelfromfile
  14. //Checkpostcondition
  15. assert(pVehicle->GetWheelCount()==4);//ThiswillberemovedinReleasemode!
  16. //Returnpointertothevehicleobject
  17. returnpVehicle;
  18. }
#include <assert.h> // This function reads a sports car's model from a file CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName) { CVehicle* pVehicle = NULL; // Pointer to vehicle object // Check preconditions assert(szFileName!=NULL); // This will be removed by preprocessor in Release mode! assert(_tcslen(szFileName)!=0); // This will be removed in Release mode! // Open the file FILE* f = _tfopen(szFileName, _T("rt")); // Create new CVehicle object pVehicle = new CVehicle(); // Read vehicle model from file // Check postcondition assert(pVehicle->GetWheelCount()==4); // This will be removed in Release mode! // Return pointer to the vehicle object return pVehicle; }

看看上述的代码,Asserts能够在debug模式下检测我们的程序,在release 模式下却不能。所以我们还是不得不用if()来这步检测操作。正确的代码如下;

  1. CVehicle*ReadVehicleModelFromFile(LPCTSTRszFileName,)
  2. {
  3. CVehicle*pVehicle=NULL;//Pointertovehicleobject
  4. //Checkpreconditions
  5. assert(szFileName!=NULL);//ThiswillberemovedbypreprocessorinReleasemode!
  6. assert(_tcslen(szFileName)!=0);//ThiswillberemovedinReleasemode!
  7. if(szFileName==NULL||_tcslen(szFileName)==0)
  8. returnNULL;//Invalidinputparameter
  9. //Openthefile
  10. FILE*f=_tfopen(szFileName,_T("rt"));
  11. //CreatenewCVehicleobject
  12. pVehicle=newCVehicle();
  13. //Readvehiclemodelfromfile
  14. //Checkpostcondition
  15. assert(pVehicle->GetWheelCount()==4);//ThiswillberemovedinReleasemode!
  16. if(pVehicle->GetWheelCount()!=4)
  17. {
  18. //Oops...aninvalidwheelcountwasencountered!
  19. deletepVehicle;
  20. pVehicle=NULL;
  21. }
  22. //Returnpointertothevehicleobject
  23. returnpVehicle;
  24. }
CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName, ) { CVehicle* pVehicle = NULL; // Pointer to vehicle object // Check preconditions assert(szFileName!=NULL); // This will be removed by preprocessor in Release mode! assert(_tcslen(szFileName)!=0); // This will be removed in Release mode! if(szFileName==NULL || _tcslen(szFileName)==0) return NULL; // Invalid input parameter // Open the file FILE* f = _tfopen(szFileName, _T("rt")); // Create new CVehicle object pVehicle = new CVehicle(); // Read vehicle model from file // Check postcondition assert(pVehicle->GetWheelCount()==4); // This will be removed in Release mode! if(pVehicle->GetWheelCount()!=4) { // Oops... an invalid wheel count was encountered! delete pVehicle; pVehicle = NULL; } // Return pointer to the vehicle object return pVehicle; }
  • Checking Return Code of a Function

断定一个函数执行一定成功是一种常见的错误。当你调用一个函数的时候,建议检查下返回代码和返回参数的值。如下代码持续调用Windows API ,程序是否继续执行下去依赖于该函数的返回结果和返回参数值。

  1. HRESULThres=E_FAIL;
  2. IWbemServices*pSvc=NULL;
  3. IWbemLocator*pLoc=NULL;
  4. hres=CoInitializeSecurity(
  5. NULL,
  6. -1,//COMauthentication
  7. NULL,//Authenticationservices
  8. NULL,//Reserved
  9. RPC_C_AUTHN_LEVEL_DEFAULT,//Defaultauthentication
  10. RPC_C_IMP_LEVEL_IMPERSONATE,//DefaultImpersonation
  11. NULL,//Authenticationinfo
  12. EOAC_NONE,//Additionalcapabilities
  13. NULL//Reserved
  14. );
  15. if(FAILED(hres))
  16. {
  17. //Failedtoinitializesecurity
  18. if(hres!=RPC_E_TOO_LATE)
  19. returnFALSE;
  20. }
  21. hres=CoCreateInstance(
  22. CLSID_WbemLocator,
  23. 0,
  24. CLSCTX_INPROC_SERVER,
  25. IID_IWbemLocator,(LPVOID*)&pLoc);
  26. if(FAILED(hres)||!pLoc)
  27. {
  28. //FailedtocreateIWbemLocatorobject.
  29. returnFALSE;
  30. }
  31. hres=pLoc->ConnectServer(
  32. _bstr_t(L"ROOT\\CIMV2"),//ObjectpathofWMInamespace
  33. NULL,//Username.NULL=currentuser
  34. NULL,//Userpassword.NULL=current
  35. 0,//Locale.NULLindicatescurrent
  36. NULL,//Securityflags.
  37. 0,//Authority(e.g.Kerberos)
  38. 0,//Contextobject
  39. &pSvc//pointertoIWbemServicesproxy
  40. );
  41. if(FAILED(hres)||!pSvc)
  42. {
  43. //Couldn'tconectserver
  44. if(pLoc)pLoc->Release();
  45. returnFALSE;
  46. }
  47. hres=CoSetProxyBlanket(
  48. pSvc,//Indicatestheproxytoset
  49. RPC_C_AUTHN_WINNT,//RPC_C_AUTHN_xxx
  50. RPC_C_AUTHZ_NONE,//RPC_C_AUTHZ_xxx
  51. NULL,//Serverprincipalname
  52. RPC_C_AUTHN_LEVEL_CALL,//RPC_C_AUTHN_LEVEL_xxx
  53. RPC_C_IMP_LEVEL_IMPERSONATE,//RPC_C_IMP_LEVEL_xxx
  54. NULL,//clientidentity
  55. EOAC_NONE//proxycapabilities
  56. );
  57. if(FAILED(hres))
  58. {
  59. //Couldnotsetproxyblanket.
  60. if(pSvc)pSvc->Release();
  61. if(pLoc)pLoc->Release();
  62. returnFALSE;
  63. }
HRESULT hres = E_FAIL; IWbemServices *pSvc = NULL; IWbemLocator *pLoc = NULL; hres = CoInitializeSecurity( NULL, -1, // COM authentication NULL, // Authentication services NULL, // Reserved RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation NULL, // Authentication info EOAC_NONE, // Additional capabilities NULL // Reserved ); if (FAILED(hres)) { // Failed to initialize security if(hres!=RPC_E_TOO_LATE) return FALSE; } hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres) || !pLoc) { // Failed to create IWbemLocator object. return FALSE; } hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace NULL, // User name. NULL = current user NULL, // User password. NULL = current 0, // Locale. NULL indicates current NULL, // Security flags. 0, // Authority (e.g. Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy ); if (FAILED(hres) || !pSvc) { // Couldn't conect server if(pLoc) pLoc->Release(); return FALSE; } hres = CoSetProxyBlanket( pSvc, // Indicates the proxy to set RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { // Could not set proxy blanket. if(pSvc) pSvc->Release(); if(pLoc) pLoc->Release(); return FALSE; }
  • Using Smart Pointers

如果你经常使用用享对象指针,如COM 接口等,那么建议使用智能指针来处理。智能指针会自动帮助你维护对象引用记数,并且保证你不会访问到被删除的对象。这样,不需要关心和控制接口的生命周期。关于智能指针的进一步知识可以看看Smart Pointers - What, Why, Which?Implementing a Simple Smart Pointer in C++这两篇文章。

如面是一个展示使用ATL's CComPtr template 智能指针的代码,该部分代码来至于MSDN。

  1. #include<windows.h>
  2. #include<shobjidl.h>
  3. #include<atlbase.h>//ContainsthedeclarationofCComPtr.
  4. intWINAPIwWinMain(HINSTANCEhInstance,HINSTANCE,PWSTRpCmdLine,intnCmdShow)
  5. {
  6. HRESULThr=CoInitializeEx(NULL,COINIT_APARTMENTTHREADED|
  7. COINIT_DISABLE_OLE1DDE);
  8. if(SUCCEEDED(hr))
  9. {
  10. CComPtr<IFileOpenDialog>pFileOpen;
  11. //CreatetheFileOpenDialogobject.
  12. hr=pFileOpen.CoCreateInstance(__uuidof(FileOpenDialog));
  13. if(SUCCEEDED(hr))
  14. {
  15. //ShowtheOpendialogbox.
  16. hr=pFileOpen->Show(NULL);
  17. //Getthefilenamefromthedialogbox.
  18. if(SUCCEEDED(hr))
  19. {
  20. CComPtr<IShellItem>pItem;
  21. hr=pFileOpen->GetResult(&pItem);
  22. if(SUCCEEDED(hr))
  23. {
  24. PWSTRpszFilePath;
  25. hr=pItem->GetDisplayName(SIGDN_FILESYSPATH,&pszFilePath);
  26. //Displaythefilenametotheuser.
  27. if(SUCCEEDED(hr))
  28. {
  29. MessageBox(NULL,pszFilePath,L"FilePath",MB_OK);
  30. CoTaskMemFree(pszFilePath);
  31. }
  32. }
  33. //pItemgoesoutofscope.
  34. }
  35. //pFileOpengoesoutofscope.
  36. }
  37. CoUninitialize();
  38. }
  39. return0;
  40. }
#include <windows.h>#include <shobjidl.h> #include <atlbase.h> // Contains the declaration of CComPtr.int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow){ HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (SUCCEEDED(hr)) { CComPtr<IFileOpenDialog> pFileOpen; // Create the FileOpenDialog object. hr = pFileOpen.CoCreateInstance(__uuidof(FileOpenDialog)); if (SUCCEEDED(hr)) { // Show the Open dialog box. hr = pFileOpen->Show(NULL); // Get the file name from the dialog box. if (SUCCEEDED(hr)) { CComPtr<IShellItem> pItem; hr = pFileOpen->GetResult(&pItem); if (SUCCEEDED(hr)) { PWSTR pszFilePath; hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); // Display the file name to the user. if (SUCCEEDED(hr)) { MessageBox(NULL, pszFilePath, L"File Path", MB_OK); CoTaskMemFree(pszFilePath); } } // pItem goes out of scope. } // pFileOpen goes out of scope. } CoUninitialize(); } return 0;}
  • Using == Operator Carefully

先来看看如下代码;

  1. CVehicle*pVehicle=GetCurrentVehicle();
  2. //Validatepointer
  3. if(pVehicle==NULL)//Using==operatortocomparepointerwithNULL
  4. returnFALSE;
  5. //Dosomethingwiththepointer
  6. pVehicle->Run();
CVehicle* pVehicle = GetCurrentVehicle(); // Validate pointer if(pVehicle==NULL) // Using == operator to compare pointer with NULL return FALSE; // Do something with the pointer pVehicle->Run();

上面的代码是正确的,用语指针检测。但是如果不小心用“=”替换了“==”,如下代码;

  1. CVehicle*pVehicle=GetCurrentVehicle();
  2. //Validatepointer
  3. if(pVehicle=NULL)//Oops!Amistypinghere!
  4. returnFALSE;
  5. //Dosomethingwiththepointer
  6. pVehicle->Run();//Crash!!!
CVehicle* pVehicle = GetCurrentVehicle(); // Validate pointer if(pVehicle=NULL) // Oops! A mistyping here! return FALSE; // Do something with the pointer pVehicle->Run(); // Crash!!!

看看上面的代码,这个的一个失误将导致程序崩溃。

这样的错误是可以避免的,只需要将等号左右两边交换一下就可以了。如果在修改代码的时候,你不小心产生这种失误,这个错误在程序编译的时候将被检测出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值