我们之前学习了构造函数,类的构造函数用于对象的初始化。构造函数与类同名并且没有返回值,构造函数在对象定义时自动被定义。那么我们就思考下:1、如何判断构造函数的执行结果呢?2、在构造函数中执行 return 语句会发生什么呢?3、构造函数执行结束是否意味着对象构造成功呢?

我们带着问题来看看下面程序会怎么执行

#include<stdio.h>classTest{private:intmi;intmj;public:Test(inti,intj){mi=i;return;mj=j;}intgetI(){returnmi;}intgetJ(){returnmj;}};intmain(){Testt(1,2);printf("t.mi=%d\n",t.getI());printf("t.mj=%d\n",t.getJ());return0;}

我们在构造函数中直接 return,看看编译是否会通过

我们看到编译是通过的,但是它的结果和我们想的不一样。那么我们会想这返回的是个错误的对象,有什么办法杜绝呢?加个私有的 bool 类型成员变量 mStatus 来判断下它的状态,如果构造函数执行完成便将它置为 true,初始化为 false。在 main 函数中先判断它的状态是否为 true,如果是则执行打印。程序如下

#include<stdio.h>classTest{private:intmi;intmj;boolmStatus;public:Test(inti,intj):mStatus(false){mi=i;return;mj=j;mStatus=true;}intgetI(){returnmi;}intgetJ(){returnmj;}boolgetStatus(){returnmStatus;}};intmain(){Testt(1,2);if(t.getStatus()){printf("t.mi=%d\n",t.getI());printf("t.mj=%d\n",t.getJ());}else{printf("failedtoinittot!!!\n");}return0;}

我们看看编译结果

确实是初始化失败了。关于构造函数,我们可能不知道的几个点:1、只提供自动初始化成员变量的机会;2、不能保证初始化逻辑一定成功;3、执行 return 语句后构造函数立即结束。由此可见,构造函数能决定的知识对象的初始化状态,而不是对象的诞生!!

在 C++ 中有半成品的概念,顾名思义就是未初始化完成的对象。半成品对象是合法的 C++ 对象,也是 Bug 的重要来源之一。在我们之前创建的数组类中,如果在构造函数中申请数组大小得不到成功执行,那么就会莫名的得到段错误。但是它是不确定的,一般而言,这种情况很少,所以也就很不好调试。

那么依据工程经验,这时我们便可将构造过程分为:与资源无关的初始化操作,也就是不可能出现异常情况的操作;还有就是需要使用系统资源的操作,可能出现异常情况,如:内存申请,访问文件等。下面我们以一幅图来说明二阶构造的顺序

那么我们可以看出如果在进行系统资源申请操作时出错,我们便删除半成品对象,返回 NULL。这样我们便可以避免这类的 Bug。下来我们以代码为例进行分析说明

#include<stdio.h>classTowPhassCons{private:TowPhassCons()//第一阶段构造函数{}boolconstruct()//第二阶段构造函数{returntrue;}public:staticTowPhassCons*NewInstance();//对象创建函数};TowPhassCons*TowPhassCons::NewInstance(){TowPhassCons*ret=newTowPhassCons();//若第二阶段构造失败,返回NULLif(!(ret&&ret->construct())){deleteret;ret=NULL;}returnret;}intmain(){TowPhassConsobj;//TowPhassConsobj=newTowPhassCons();/*TowPhassCons*obj=TowPhassCons::NewInstance();printf("obj=%p\n",obj);deleteobj;*/return0;}

我们先来这样试试平时我们直接创建对象的方法,看看编译可以通过吗

它说构造函数是个私有函数,我们不能直接调用。再来试试第 35 行那样的创建对象呢

还是报一样的错误。那么我们再来试试最后一种,调用二阶构造函数

那么编译是通过的。如果我们试试在 construct 函数中直接返回 false 呢

那么对象 obj 就会指向为空。下来我们就是用二阶构造的思想来加强下我们之前所写的数组类


IntArray.h 源码

#ifndef_INTARRAY_H_#define_INTARRAY_H_classIntArray{private:intm_length;int*m_pointer;IntArray(intlen);boolconstruct();public:staticIntArray*NewInstance(intlength);intlength();boolget(intindex,int&value);boolset(intindex,intvalue);~IntArray();};#endif


IntArray.cpp 源码

#include"IntArray.h"IntArray::IntArray(intlen){m_length=len;}boolIntArray::construct(){boolret=true;m_pointer=newint[m_length];if(m_pointer){for(inti=0;i<m_length;i++){m_pointer[i]=0;}}else{ret=false;}returnret;}IntArray*IntArray::NewInstance(intlength){IntArray*ret=newIntArray(length);if(!(ret&&ret->construct())){deleteret;ret=0;}returnret;}intIntArray::length(){returnm_length;}boolIntArray::get(intindex,int&value){boolret=(0<=index)&&(index<=length());if(ret){value=m_pointer[index];}returnret;}boolIntArray::set(intindex,intvalue){boolret=(0<=index)&&(index<=length());if(ret){m_pointer[index]=value;}returnret;}IntArray::~IntArray(){delete[]m_pointer;}


test.cpp 源码

#include<stdio.h>#include"IntArray.h"intmain(){IntArray*a=IntArray::NewInstance(5);printf("a.length=%d\n",a->length());for(inti=0;i<a->length();i++){a->set(i,i+1);}for(inti=0;i<a->length();i++){intv=0;a->get(i,v);printf("a[%d]=%d\n",i,v);}deletea;return0;}

我们编译下,看看结果

结果和我们所想的是一样的。通过对二阶构造的学习,总结如下:1、构造函数只能决定对象的初始化状态,构造函数中初始化操作的失败并不影响对象的诞生;2、初始化不完全的半成品对象是 Bug 的重要来源;3、二阶构造人为的将初始化过程分为两部分,它能确保创建的对象都是完整初始化的。


欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。