难产是谁的责任?

来源:互联网 发布:淘宝app双11标 编辑:程序博客网 时间:2024/06/12 00:29

我写一个小文,同样在“代码片段”里,题为:《常量成员函数限定的是什么?》。讲的是,在常量成员函数里,可以修改指针成员所指向的数据,因为什么什么的。示例代码如下:

Code:
  1. class Coo2    
  2. {      
  3.  public:     
  4.  Coo2() : p(new int(0)) {}    
  5.  ~Coo2() {delete p;}    
  6.       
  7.     int const * getP() const //常量成员函数      
  8.     {      
  9.          *p = 1;     //编译OK     
  10.          return this->p;      
  11.     }      
  12.  private:    
  13.   int* p;    
  14. };   

但有郭同学看完后评论: “我的天啊,这种有问题的代码,你竟然认为编译通过,就是完事了么,要是构造函数出现异常,那么是不是就出现内存泄漏问题了。”

嘿嘿,我一下子坏笑起来了。先说个半真半假的故事吧:

      那天我家小区来了不知从哪个省流浪过来三个可怜的小姐妹。最小的妹妹表演骑独轮车过酒瓶,老二表演踢八仙桌(人仰卧在地,瘦瘦的双脚有着神奇的力量,一米见方的桌子在空中调整地,各个角度地翻转)。 大姐表演顶盘子,桌子上摆一个球,球上跨一木板,木板前端不断放盘子,大姐站在板上面,一踩,那盘子就上了头顶层层叠叠,很是惊险,我看得真揪心……
  

       以上内容基本是真实的,接着来开始假的内容:

        三位姐妹表演完毕后,大姐走到人前唱个诺:“各位叔叔阿姨兄弟姐妹,要是觉得我们仨演得还行,就请帮个忙,赏点小钱吧……” 人群一片叫好,给钱者众。很快走到我面前,我坚决不给钱,围观者或有迷惑不解,我答曰:

   “我的天啊,这种表演也敢伸手要钱!  衣服如此之脏兮兮, 人也长得如此之不性感!看看你们的三围!那也敢叫作表演吗!”

          --------------------------------------------------------

    同样是表演,关注点不一样吧,原来“我”一直盯着人家杂技演员的胸部看! 嘿嘿,郭同学在你看俺这段代码和小文时,肯定是一直盯着俺的敏感位置看了。别这样噢,我会脸红的。。。

    不过我还是支持你的,所谓 “世界是相同的,然而眼睛不同,所以看到的世界也就不同”。 (最近我越来越像文学青年了~~)

     说点正经的,Coo2 的类设计(如果这样一个小例子也算得上设计的话),我认为不会有你担心的问题(内存泄漏)。 它的构造过程就是一个 new 语句,并且new的是简单对象(int数据)。 它要么成功,要么失败。

     再说一遍: 构造过程就一句代码,一句"new内置类型数据”的代码。所以,这个new过程成功了,就代表构造成功了。(除非那时候操作系统蓝屏了或机器掉电了等等不可控原因)。构造成功,接下来交给这个对象的使用者了,只要他最终调用了delete,则对象的析构函数被调用,而我在析构函数~Coo2里,已经做了这个对象理应做的一切:delete p;

        (我算是听话的了,很多C++大牛写的课程,为了让读者专注当前要谈的问题,或者只是不想写一堆与当前主题无关的代码,他们根本就不写这些常规的代码)。

       另一种情况,就是你说的:“要是构造函数出现异常……” 。如果是你是在说C++异常的话 ,那这个构造函数只能是唯一的那句语句可能出异常。也就是那句“new内置类型数据”的代码抛出异常。嗯。我们都了解C++的new行为在默认情况下,失败时会抛出异常。当然,我们也都了解,如果这个异常没有被调用者代码catch到的话……那么,C行为将要出现,一个在C的世界里就定义好的的一个普通的函数(正好考一下C/C++初学者,这个函数叫什么名字?)将会被调用,然后呢,基本上是这个程序的世界毁灭了……

      如果这程序的世界没有over掉,这时,我们要关注你说的那个问题了:“那么是不是就出现内存泄漏问题了” ?

     我们已经谈了一个C++标准:new失败后的行为,现在说另一个标准: 当一个对象在构造时,构造函数失败了,会怎样? Delphi的朋友会说,肯定是调用它的析构函数啊。 不过C++是另一套标准:构造失败,则这个对象是不完整的,而C++坚决不允许析构一个“不完整的对象”,于是乎,如果像这样的构造函数:

Code:
  1. struct A  
  2.     {  
  3.            A()  
  4.           {  
  5.                  p1 = new int[100];  
  6.                  p2 = new int[100000]; //  
  7.           };  
  8.          //... 学一下大牛,我不写后面的常规代码了。  
  9.     };  

            如果上面的 (05 行)p1成功地占用了100个整数的内存, 但在(06 行 )new p2 时失败, 则前面p1的内存就泄漏了,因为C++标准不让此时调用析构函数啊。。。。

             呵呵。郭同学会有那个评论的第一个可能,应是在指这一种情况吧?   如果是,那那那与我原文的代码何干啊。。。我这里正好就是一个超简单的示例代码,里面就一个分配内存的操作,而且正好我new的还是内置类型的数据,所以,它要是成功了,它就会被在析构函数中释放;它要是失败了,那它就不会占用内存,何来“内存就泄漏了”的担忧呢?

            也许吧,你只是希望我写这样的代码:

Code:
  1. class Coo2  
  2. {  
  3.     public:  
  4.        Coo2()   
  5.               try : p (new int(0))  //对付构造初始化列表的异常  
  6.        {}  
  7.        catch(...)  
  8.        {}  
  9.   
  10.        //继续装牛人,不写了...免得又出什么事 
  11. };  

            这我就要虚构另外一个故事了:

           有个胎儿近10月了正要出发,突然听到有个医生肚子外面对他喊:“亲爱的胎儿! 听着,我要向你提一个规定! 如果在出生过程中,因为医院突然断电, 或者因为你个头太大, 或者因为接生你的医生正在偷菜,或者因为 ……等等异常原因,造成你难产挂掉, 请你一定要在挂掉前负责处理好这些异常!包括清理好因为这些异常而造成一切资源占用!! 你愿意遵守这个约定,你就出来吧,否则,你就别出来了!

           需要由“被构造”的对象自行处理构造过程中发生的异常,存在这种需求吗? 存在!在一些非常严格及恶劣的环境下会要求。但通常多数库,要么绕个弯,不在构造函数里分配成员内存(称为二次构造,典型的如MFC或wxWidgets库的Create函数),要么就通通交给外部的世界,就像我们人类的世界,可爱的baby们,你们放心地出来吧……

-----------------------------------------

           还是说得复杂了,对于我原文中Coo2那么简单的构造过程,有必要搞一切吗?

          就算我的Coo2中有new 1个,两个,甚至更多个对象, 我一定要在我的每一个例子里,都搞一个异常处理吗?

          我的天啊!

 如果您想与我交流,请点击如下链接成为我的好友:
http://student.csdn.net/invite.php?u=112600&c=f635b3cf130f350c

 

 

原创粉丝点击