构造函数中的异常处理

来源:互联网 发布:用php做99乘法表 编辑:程序博客网 时间:2024/05/16 12:35

构造函数中的异常处理

    异常处理,在C++里是一种很细致的工作,必须认真的考虑到每一种出错的可能,并针对之做出合适的处理。这对于构造函数中的异常处理更是如此!构造函数用于对象的初始创建,如果抛出异常,那么它的行为可能不太好预测~特别是资源泄漏问题!

    假定我们要建个人信息类,包含一个人的姓名,地址,照片和电话号码,这个类看起来应该是这样的:

     class Info

      {

           private:

               string Name;

               string Address;

               Image* P_Image;

               Phone* P_Phone;

          public:

               Info(const string& name,const string& addr,const string& image,\

           const string& phone):Name(name),Address(addr),P_Image(0)\

           P_Phone(0)

               {

                       if(image!=" ")

                             P_Image=new Image(image);

                       if(phone!=" ")

                             P_Phone=new Phone(phone);

              }

             ~Info()

              {

                     delete P_Image;

                     delete P_Phone;

              }

       };

    这里假定Image类和Phone类都有一个以字符串为参数的构造函数,如果Info的构造函数中P_Image的new语句由于出现内存不足而导致的异常时,那么可能事情并没有那么糟,无非就是Info对象无法构造完成,不能生成一个对象而已。但如果是P_Phone的那句new语句出现异常时,由于P_Image已经初始化完成,此时必然会产生内存泄漏。因为无法释放P_Image的内存部分,这里绝对不要妄想会调用析构函数,对于一个尚未初始化完成的对象,那么析构函数又如何能被调用呢~

    所以,异常处理就发挥了作用,一个很合适的try-catch子块可以很好的解决这个问题,无非是多加入几行代码而已:

    //构造函数,成员初始化列表不变

    {

           try

             {

                 if(image!=" ")

                             P_Image=new Image(image);

                       if(phone!=" ")

                             P_Phone=new Phone(phone);

              }catch(...)

              {

                    delete P_Image;

                    delete P_Phone;

                     throw;          //传播异常,如果没有,则是吞掉异常

               }

    这样,无论是谁抛出了异常,都不会导致资源泄漏问题。这里catch子块的部分与构造函数几乎一样,为了避免代码的重复,通常做法是将两个delete子句写入一个函数,并放入到类中的私有部分。啊?你问为什么Name,Address不需要我们手动释放?好吧,它们会在类的构造函数调用之前就构造好,一旦出现异常,它们会像已经构造好的对象一样得到释放,而并不要你插手。这也部分归功于初始化成员列表。

    幸福不会来的那么容易的~如果你的Image和Phone是个const指针又如何呢?对于const成员变量,必须在成员初始化类表中给予初始化,那么这个构造函数看起来应该是这样的:

    Info(const string& name,const string& addr,const string& image,const string&\

   phone):Name(name),Address(addr),P_Image(image==" "?0:new Image(image)\

   P_Phone(phone==" "?0:new Phone(phone)){}

    由于成员初始化列表中只能出现表达式而不能出现语句,上述的try-catch子块就没有办法用来避免异常的出现啦~这里如果new部分出现异常,那么资源泄漏就没有办法避免了~

    C++里有个规则:任何问题都可以通过间接层来得到解决。实现到这个问题就是:我们可以引出两个私有函数,用于产生Image指针和Phone指针,并用来初始化const变量,而在这两个函数中调用try-catch子块进行异常处理:

     //位于private部分

     Image* CreatImage(const string& image)

     {

             if(image!=" ")

                  return new Image(image);

             else 

                  return 0;   

       }  

      //注意差别

        Phone* CreatPhone(const string& phone)

       {

            try

            {

                if(phone!=" ")

                        return new Phone(phone);

                else

                         return 0;

             }catch(...)

             {

                    delete P_Image;

                    throw;

              }

       }

    而相应的构造函数则变为对函数的调用:

         Info(const string& name,const string& addr,const string& image,\

         const string& phone):Name(name),Address(addr),\

         P_Image(CreatImage(image)),P_Phone(CreatPhone(phone)){}

    是不是感觉很奇特呢~不过最简单的方法还是以对象来管理资源,利用标准库里的auto_ptr可以更好的解决这个问题,其实现不过是将P_Image和P_Phone生成为atuo_ptr对象而已。如果出现异常,则对象就可以得到析构,从而有效的避免了资源泄漏的问题。

 

原创粉丝点击