zoukankan      html  css  js  c++  java
  • 第27课二阶构造模式(上)---------出现的背景

    构造函数的回顾
    关于构造函数
    ——类的构造函数用于对象的初始化
    ——构造函数与类同名并且没有返回值
    ——构造函数在对象定义时自动被调用

    问题:
    1. 如何判断构造函数的执行结果?
       目前来说,没有办法来判断构造函数的执行结果
    2. 在构造函数中执行return语句会发生什么?
        在构造函数中可以存在return语句,return之后下面的代码就无法执行,会影响对象的初始状态
    3. 构造函数执行结束是否意味着对象构造成功?
       对象的诞生与构造函数的执行结果是没有任何关系的。如果构造函数没有执行成功,只会影响它的初始状态,并不影响对象的创建。

    异常的构造函数

    #include <stdio.h>
    
    class Test
    {
        int mi;
        int mj;public:
        Test(int i, int j)
        {
            mi = i;
            
            return;
            
            mj = j;
            
        }
        int getI()
        {
            return mi;
        }
        int getJ()
        {
            return mj;
        }
        
    };
    
    int main()
    {  
        Test t1(1, 2);
        
        printf("t1.mi = %d
    ", t1.getI());
        printf("t1.mj = %d
    ", t1.getJ());
        return 0;
    }

    在构造函数中执行return语句:对象会被创建成功,但是对象的初始状态会发生异常。例如在这里的本意是将对象的成员mj赋值为2,但是得到的结果却是随机值。

    下面可以这样做:

    #include <stdio.h>
    
    class Test
    {
        int mi;
        int mj;
        bool mStatus;
    public:
        Test(int i, int j) : mStatus(false)
        {
            mi = i;
            
            return;
            
            mj = j;
            
            mStatus = true;
        }
        int getI()
        {
            return mi;
        }
        int getJ()
        {
            return mj;
        }
        int status()
        {
            return mStatus;
        }
    };
    
    int main()
    {  
        Test t1(1, 2);
        
        if( t1.status() )
        {
            printf("t1.mi = %d
    ", t1.getI());
            printf("t1.mj = %d
    ", t1.getJ());
        
        }
        
        return 0;
    }

    这种解决方案确实可以,就是强行的让构造函数有一个返回值。并且手工的调用status来得到构造函数的返回值。这个似乎可以解决问题,但是不够优美。

    通过上面的实验,可以得到:
    构造函数
    ——只提供自动初始化成员变量的机会
    ——不能保证初始化逻辑一定成功
    ——执行return语句后构造函数立即结束。

    构造函数能决定的只是对象的初始状态,而不是对象的诞生。

    半成品对象的概念
    ——初始化操作不能按照预期完成而得到的对象
    ——半成品对象是合法的C++对象,也是Bug的重要来源

    办成品对象的危害

    还是以之前创建的那个数组类为例,进行分析。

    #include "IntArray.h"
    
    IntArray::IntArray(int len)
    {
        m_pointer = new int[len];  
        
        for(int i=0; i<len; i++)
        {
            m_pointer[i] = 0;
        }
        
        m_length = len;
    }
    
    IntArray::IntArray(const IntArray& obj)
    {
        m_length = obj.m_length;
        
        m_pointer = new int[obj.m_length];
        
        for(int i=0; i<obj.m_length; i++)
        {
            m_pointer[i] = obj.m_pointer[i];
        }
    }
    
    int IntArray::length()
    {
        return m_length;
    }
    
    bool IntArray::get(int index, int& value)
    {
        bool ret = (0 <= index) && (index < length());
        
        if( ret )
        {
            value = m_pointer[index];
        }
        
        return ret;
    }
    
    bool IntArray::set(int index, int value)
    {
        bool ret = (0 <= index) && (index < length());
        
        if( ret )
        {
            m_pointer[index] = value;
        }
        
        return ret;
    }
    
    IntArray::~IntArray()
    {
        delete[]m_pointer;
    }
    分析下面这段代码:
    IntArray::IntArray(int len) { m_pointer = new int[len]; //申请堆空间 for(int i=0; i<len; i++) { m_pointer[i] = 0; } m_length = len; }
    问题:每次申请堆空间时,你能保证每次都申请成功吗?也许1万次了,出现几次不成功。看上去概率很低,但是我们也不能容忍,你可能会这样去做。
    IntArray::IntArray(int len)
    {
        m_pointer = new int[len];   //申请堆空间
        if(m_pointer)
        {  for(int i=0; i<len; i++)
          {
              m_pointer[i] = 0;
          }
        }
        m_length = len;
    }
    这样当申请失败了,就不需要执行花括号里面的代码了。看似没有什么问题,也合情合理。
    注意:前面已经说过,构造函数内执行是否成功,与对象是否创建没有什么关系。上面这个例子中构造函数体内没有执行成功,肯定会影响对象的初始状态,但是用户并不知道这个对象的成员有异常。当用户拿到这个类时,它并不知道你花括号里面的内容没有执行,该怎么用就怎么用。
    看下面的使用:
    int main()
    {
      IntArray a(5);    
      a.set(0,1);
    }
    编译没有问题,运行时悲剧就产生了。段错误。出现的原因就是在构造函数中
    m_pointer没有申请成功,但是下面又使用了它。
    
    
    
    
    
    
    












  • 相关阅读:
    nat下没法ping通virutalbox中的centos7,解决共享文件夹问题
    深度学习的精确率和召回率,浅显的例子
    python发送邮件心得体会
    ubuntu 16.04 搭建tigervnc
    交叉编译7zip过程
    git使用经验汇总
    python 开发环境部署
    Ubuntu设置su和sudo为不需要密码 (摘录自别处)
    ubuntu 16.04 安装wechat, chrome等
    andorid开发build.gradle 增加几种产品的方法
  • 原文地址:https://www.cnblogs.com/-glb/p/11875853.html
Copyright © 2011-2022 走看看