欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【C++深度解析】17、二阶构造模式

程序员文章站 2022-05-29 17:49:47
...

1 构造函数 return 会怎么样?

首先我们思考一个问题?在构造函数中执行 return 语句会发生什么?构造函数执行结束是否意味着对象构造成功?

编程实验:异常的构造函数

// 17-1.cpp
#include<stdio.h>
class Test
{
public:
    Test(int i, int j)
    {
        x = i;
        return;
        y = j;
    }
    int getX() { return x; }
    int getY() { return y; }
private:
    int x;
    int y;
};
int main()
{
    Test t(1, 2);
    printf("t.getX() = %d\n", t.getX());
    printf("t.getY() = %d\n", t.getY());
    return 0;
}

在构造函数中完成了对 x 的初始化后构造函数 return,也就是说构造函数没有完成全部的初始化任务,这会出现什么结果呢?

$ g++ 17-1.cpp -o 17-1
$ ./17-1
t.getX() = 1
t.getY() = 32766

从结果可以看到,虽然构造函数异常,但是依然生成了对象,x 的值完成初始化,y 的值为随机值。

也即是说,构造函数只提供自动初始化成员变量的机会,并不保证初始化逻辑一定成功。执行 return 语句后构造函数立即结束。

1.1 半成品对象

半成品对象概念

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

2 二阶构造

构造函数可分为两种:

  • 资源无关的初始化操作,不可能产生异常
  • 需要使用系统资源的操作,可能产生操作,如:内存申请,访问文件

二阶构造如下图所示:
创建对象时,先进行第一阶段的构造,完成资源无关的初始化操作。然后再进行二阶段的构造,申请系统资源。如果申请成功,就返回这个对象,不成功时删除半成品对象

【C++深度解析】17、二阶构造模式

具体操作如下:
将类名的构造函数作为第一阶段的构造函数,完成资源无关的初始化操作,另外定义一个第二阶段的构造函数,当申请系统资源成功时返回 true,失败返回 false。第一阶段的构造函数和第二阶段的构造函数均为私有。

定义一个公有的对象创建函数,首先调用第一阶段的构造函数创建对象,再调用第二阶段的构造函数,根据返回值判断构造是否成功。成功返回对象的指针,失败返回 NULL。

【C++深度解析】17、二阶构造模式
下面我们就来编译运行一下:


// 17-2.cpp
#include<stdio.h>
class TwoPhaseCons
{
private:
    TwoPhaseCons()
    {
    }
    bool construct()
    {
        return true;
    }
public:
    static TwoPhaseCons* NewInstance();
};
TwoPhaseCons* TwoPhaseCons::NewInstance()
{
    TwoPhaseCons* ret = new TwoPhaseCons();
    if (!(ret && ret->construct()))
    {
        delete ret;
        ret = NULL;
    }
    return ret;
}
int main()
{
    TwoPhaseCons* obj = TwoPhaseCons::NewInstance();
    printf("obj = %p\n", obj);
    return 0;
}
$ g++ 17-2.cpp -o 17-2
$ ./17-2
obj = 0x55b7ce853e70

2.1 数组类改进

前面我们完成了数组类,类中需要申请数组空间,属于申请系统资源,这里我们用二结构造改进一下。

// IntArray.h
#ifndef _INTARRAY_H_
#define _INRARRAY_H_
class IntArray
{
private:
    IntArray(int len);
    bool construct();
    IntArray(const IntArray& obj);
    int m_length;
    int* m_pointer;
public:
    static IntArray* NewInstance(int length);
    int length();
    bool get(int index, int& value);
    bool set(int index, int value);
    ~IntArray();
};
#endif
#include"IntArray.h"
#include<stdio.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 < m_length; i++)
        {
            m_pointer[i] = 0;
        }
    }
    else
    {
        ret = false;
    }
    return ret;
}
IntArray* IntArray::NewInstance(int length)
{
    IntArray* ret = new IntArray(length);
    if (!(ret && ret->construct()))
    {
        delete ret;
        ret = NULL;
    }
    return ret;
}
int IntArray::length()
{
    return m_length;
}
bool IntArray::get(int index, int& value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        value = m_pointer[index];
    }
    return ret;
}
bool IntArray::set(int index, int value)
{
    bool ret = (index >= 0 && index < m_length);
    if (ret)
    {
        m_pointer[index] = value;
    }
    return ret;
}
IntArray::~IntArray()
{
    delete[]m_pointer;
}
// 17-3.cpp
#include<stdio.h>
#include"IntArray.h"
int main()
{
    IntArray* t = IntArray::NewInstance(5);
    printf("t->length = %d\n", t->length());
    t->set(0, 2);
    for (int i = 0; i < 5; i++)
    {
        int v = 0;
        t->get(i, v);
        printf("t[%d] = %d\n", i, v);
    }
    delete t;
    return 0;
}

3 小结

1、构造函数中的初始化操作的失败不影响对象的诞生
2、半成品对象是 Bug 重要来源
3、二级构造将初始化过程分为两部分,确保创建的对象都是完整初始化的

相关标签: C++深度解析