编程技巧

毕业在公司上班也有两年多了,从新手到现在,在开发中用了很多的技巧。一直都没整理过,今日就好好总结下:
    一.智能指针的使用
 c++使用new分配的内存,不会在使用结束后自动释放,需要使用delete删除。在一些逻辑复杂的代码段里面,分配了内存,但是很容易在退出时释放内存。在这样的情况下可以使用智能指针,它可以保证变量在退出作用域时释放内存。
 以下的代码段在推出作用域后mem所指向的内存块将会自动释放。
 {
  char* mem = new char[32] ;
  ::std::auto_ptr<char>autoMem(mem) ;
  //do someting...
 }
 使用智能指针有什么好处呢?参考以下代码:
int getInfoByIP(const char* szIP) 
{
 char* szfPath = new char[MAX_PATH] ;
 if ( NULL == szfPath ) {
  return -1 ;
 }
 try{
  findinfo(szIP , szfPath) ;
 }
 catch(...){
  return -2 ;
 }
 try{
  SendInfo(szIP) ;
 }
 catch(...)
 {
  return -3 ;
 }

 delete []szfPath ;
 return 0 ;
}
    getInfoByIP 这个函数在正常情况下,是不会发生内存泄漏的,但是当findinfo或者SendInfo抛出异常时,szfPath使用的内存就出现泄漏了。当然可以在cathc块中使用delete 避免这种问题,但这样会造成程序不简洁,而且对以后维护增加新功能代码也增加了约束。

    二.自释放资源
 文件句柄和内存一样,打开后也需要关闭。和内存情况一样,往往会出现忘记关闭句柄的情况。在windows和linux下,句柄的使用数目是有限制的,超过了限制上限就可以造成程序崩溃,更何况文件句柄不关闭,可能会出现一些意想不到的情况。
 解决的办法可以像auto_ptr一样编写一些自释放的类,下面以文件句柄举例: 
class fHandle_auto_close
{
public :
 fHandle_auto_close(FILE* fp )
 {
  this->_fp = fp ;
 }
 ~fHandle_auto_close()
 {
  if( NULL != this->_fp )
  {
   fclose(this->_fp) ;
   this->_fp = NULL ;
  }
 }
private:
 FILE* _fp ;
}

bool FileEX::ExChangeFile(const char* szfPath)
{
 FILE* fp = fopen(szfPath , "rb") ;
 if ( NULL == fp ){
  return false ;
 }
 
 fHandle_auto_close fHandleClose(fp) ;
 //do something 
}
 fHandle_auto_close 类其实和::std::auto_ptr的作用一样,在初始化时接受一个句柄,在析构时关闭该句柄,这样在业务代码里面就不必再关注这些,在函数退出时,句柄即可自动关闭。
 实际上这个类也可以用于内存释放上,只要将句柄,初始化和析构改成相对应的内存类型和操作即可。

   三.锁的使用
 多线程编程基本上都要使用同步机制,锁是最常用的方法。使用锁也有获取和释放两个步骤。如果只获取而不释放就会造成线程阻塞。为了避免只获取而忘记释放锁,可以使用锁守卫的方法,基本思路和上面两类大致是一样的,只要改变类型和操作即可。使用模板和宏可以使用使用更方便和直观:

template<class LOCK>
class LOCK_GUARD
{
public:
 LOCK_GUARD(LOCK* lock)
  :_owner(false) 
  ,_lock(lock) 
 {
 } 
 ~LOCK_GUARD()
 {
  releaseLock() ;
 }
 bool getLock()
 {
  this->_owner = this->_lock.Lock() ;
  return this->_owner ;
 }
 bool releaseLock() 
 {
  if ( this->_owner )
  {
   bool reval = this->_lock.unLock() ; 
   if ( reval )
   {
    this->_owner = false ;
   }
   return reval ;
  }
  return true ;
 }
private:
 LOCK* _lock ;
 bool _owner ;
} ;

#define LOCK_GUARD_RETURN(LOCK_TYPE , LOCK_OBJ_NAME , LOCK , GET_LOCK_FALSE_RETURN) /
 LOCK_GUARD<LOCK_TYPE> LOCK_OBJ_NAME(LOCK) ;/
 if (!LOCK_NAME_OBJ.getLock()) /
 {/
  return GET_LOCK_FALSE_RETURN ;/
 } 
  
 使用以上的模板和宏可以更好地利用锁,使用LOCK_GUARD可以很方便地更换锁类型,只有指定的锁有合适的接口就可以更换。这对跨平台开发的代码非常有意义。通过不同的宏定义可以有更多的变化类型。
Thread_Lock lock ;
bool fixFileInfo()
{
 LOCK_GUARD_RETURN(Thread_Lock , lockfInfo , lock , false) ;
 // do something ;

      四.双检测机制
 单体对象在创建是只有一个实例,如何准确检测是否已存在实例呢?可以使用双检测机制,参考以下代码:

class test
{
public:
 static test* getInstance() 
 {
  if ( NULL == test::_instance ) // first
  {
   LOCK_GUARD_RETURN(Thread_LOCK , lockname , test::lock , NULL) ;

   if ( NULL == test::_instance ) //second 
   {
    test::_instance = new test ;
   }
  }

  return test::_instance ;
 }
protected:
 test() ;
private:
 static test* _instance ; //NULL ;
 static Thread_LOCK lock ;
}

test* instance = test::getInstance() ;

    五.宏
 遇到过一个旧程序,它里面用了很多的printf来打印调试信息。几经易手后,printf打印的信息就混乱不清了。想把这些printf去除,如果每个printf去修改,那修改的源文件太多了,后来想到了用宏定义来解决了这个问题。增加了#define printf 后,原有的printf就失去作用了。
 #define printf 
 printf("%s,%d/n" , mem,1) ;

posted on 2012-11-10 12:06  小朋友学开发  阅读(227)  评论(0编辑  收藏  举报