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

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

#include 
class Test{private:    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 t(1, 2);        printf("t.mi = %d\n", t.getI());    printf("t.mj = %d\n", t.getJ());        return 0;}

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

图片.png

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

#include 
class Test{private:    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;    }    bool getStatus()    {        return mStatus;    }};int main(){    Test t(1, 2);        if( t.getStatus() )    {        printf("t.mi = %d\n", t.getI());        printf("t.mj = %d\n", t.getJ());    }    else    {        printf("failed to init to t !!!\n");    }        return 0;}

        我们看看编译结果

图片.png

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

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

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

图片.png

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

#include 
class TowPhassCons{private:    TowPhassCons()    // 第一阶段构造函数    {    }    bool construct()  // 第二阶段构造函数    {        return true;    }public:    static TowPhassCons* NewInstance(); // 对象创建函数};TowPhassCons* TowPhassCons::NewInstance(){    TowPhassCons* ret = new TowPhassCons();        // 若第二阶段构造失败,返回 NULL    if( !(ret && ret->construct()) )    {        delete ret;                ret = NULL;    }        return ret;}int main(){    TowPhassCons obj;//    TowPhassCons obj = new TowPhassCons();/*    TowPhassCons* obj = TowPhassCons::NewInstance();        printf("obj = %p\n", obj);        delete obj;*/        return 0;}

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

图片.png

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

图片.png

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

图片.png

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

图片.png

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

IntArray.h 源码

#ifndef _INTARRAY_H_#define _INTARRAY_H_class IntArray{private:    int m_length;    int* m_pointer;        IntArray(int len);    bool construct();public:    static IntArray* NewInstance(int length);    int length();    bool get(int index, int& value);    bool set(int index, int value);    ~IntArray();};#endif

IntArray.cpp 源码

#include "IntArray.h"IntArray::IntArray(int len){    m_length = len;}bool IntArray::construct(){    bool ret = true;        m_pointer = new int[m_length];        if( m_pointer )    {        for(int i=0; i
construct()) )    {        delete ret;                ret = 0;    }        return ret;}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;}

test.cpp 源码

#include 
#include "IntArray.h"int main(){    IntArray* a = IntArray::NewInstance(5);        printf("a.length = %d\n", a->length());        for(int i=0; i
length(); i++)    {        a->set(i, i+1);    }        for(int i=0; i
length(); i++)    {        int v = 0;                a->get(i, v);                printf("a[%d] = %d\n", i, v);    }        delete a;        return 0;}

        我们编译下,看看结果

图片.png

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

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