编程技巧分享

毕业在公司上班也有两年多了,从新手到现在,在开发中用了很多的技巧。一直都没整理过,今日就好好总结下:

一.智能指针的使用

c++使用new分配的内存,不会在使用结束后自动释放,需要使用delete删除。在一些逻辑复杂的代码段里面,分配了内存,但是很容易在退出时释放内存。在这样的情况下可以使用智能指针,它可以保证变量在退出作用域时释放内存。
 以下的代码段在推出作用域后mem所指向的内存块将会自动释放。
 {
  char* mem = new char[32] ;
  ::std::auto_ptrautoMem(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_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_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去修改,那修改的源文件太多了,后来想到了