【C++深度解析】17、二阶构造模式
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 二阶构造
构造函数可分为两种:
- 资源无关的初始化操作,不可能产生异常
- 需要使用系统资源的操作,可能产生操作,如:内存申请,访问文件
二阶构造如下图所示:
创建对象时,先进行第一阶段的构造,完成资源无关的初始化操作。然后再进行二阶段的构造,申请系统资源。如果申请成功,就返回这个对象,不成功时删除半成品对象
具体操作如下:
将类名的构造函数作为第一阶段的构造函数,完成资源无关的初始化操作,另外定义一个第二阶段的构造函数,当申请系统资源成功时返回 true,失败返回 false。第一阶段的构造函数和第二阶段的构造函数均为私有。
定义一个公有的对象创建函数,首先调用第一阶段的构造函数创建对象,再调用第二阶段的构造函数,根据返回值判断构造是否成功。成功返回对象的指针,失败返回 NULL。
下面我们就来编译运行一下:
// 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++深度解析】10、进阶面向对象
下一篇: PHP常用文件函数和目录函数整理