1、构造函数的问题2、二阶构造模式
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。最常见的操作是在构造函数中为类的成员变量进行赋值,如果还想再构造函数中进行一些其他操作,可能会出现一些问题。
下面通过实例讨论一下构造函数存在的问题:
// 二阶构造模式.cpp: 定义控制台应用程序的入口点。
//
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
class Test
{
public:
Test(int a, int b)
{
m_i = a;
return;
m_j = b;
}
int getI()
{
return m_i;
}
int getJ()
{
return m_j;
}
private:
int m_i;
int m_j;
};
int main()
{
Test t(1, 2);
cout << "t.mi = " << t.getI() << endl;
cout << "t.mj = " << t.getJ() << endl;
system("pause");
return 0;
}
程序输出结果:
从结果可以看出 m_j 并没有被初始化成功,构造函数执行到 “return” 语句后退出了构造函数,从而导致 m_j =b
语句并没有执行。
关于构造函数:
由此可见,构造函数能决定的只是对象的初始化状态,而不是对象的诞生。
在C++中有半成品概念,顾名思义就是未初始化完成的对象,虽然未初始化成功,但半成品对象是合法的C++对象,也是Bug的重要来源之一。
依据工程经验,我们可以将构造过程分为以下两种:
根据上述构造过程,我们来看一下二阶构造的完整顺序:
从图中看出,如果进行系统资源申请操作出错,则应删除半成品对象并返回NULL,以避免出现Bug。请看下面二阶构造模式代码:
class SecondOrderConstruction
{
private:
SecondOrderConstruction() // 第一阶段构造函数:资源无关初始化
{
}
bool construct() // 第二阶段构造函数:与系统资源有关的初始化,如申请内存、读写文件等
{
bool ret = true;
// TODO: 系统资源相关操作并给ret赋值
return ret;
}
public:
static SecondOrderConstruction* Instance(); // 对象创建函数
};
SecondOrderConstruction* SecondOrderConstruction::Instance()
{
SecondOrderConstruction* instance = new SecondOrderConstruction();
// 第二阶段构造
if (!(instance && instance->construct()))
{
delete instance; // 申请资源失败,删除半成品对象
instance = NULL; // 返回NULL
}
return instance;
}
int main()
{
SecondOrderConstruction* obj = SecondOrderConstruction::Instance();
cout << "obj=" << obj << endl;
delete obj;
system("pause");
return 0;
}
运行结果:
函数解读: 二阶构造的方式设置类的构造函数,使得用户不能利用该类直接生成对象,因为这样操作会使得默认为构造函数被调而忽略了二阶构造的 construct() 函数。用户要生成对象需要使用类中的静态函数 Instance(),Instance() 执行 new 对象操作,此时会调用默认构造函数,在此函数中实现不可能会出现异常的操作,称为一阶构造。new 成功的前提下调用 construct() 函数,在此函数中实现可能会出现异常的操作,称为二阶构造。在确保一阶构造和二阶构造都执行成功的前提下,Instance 才返回 new 的对象的地址,反之返回NULL。