C++中构造函数之二阶构造模式

    我们在前边已经知道,构造函数是C++中的一种特殊的函数,其没有返回值,在对象定义时自动被调用,主要用于对象中成员变量的初始化,但是有个问题是,我们前期一直都是默认构造函数能正常执行完毕,但是假如构造函数中执行出错,或者提前返回,会有什么情况发生?下边来看一段代码:
 

#include <iostream>
#include <string>

using namespace std;

class test
{
private:
    int m_value1;
    int m_value2;

public:
    test()
    {
    	m_value1 = 10;
    	return;			//构造函数提前返回
    	m_value2 = 20;
    }

    int getValue1()
    {
    	return m_value1;
    }

    int getValue2()
    {
    return m_value2;
    }
};

int main()
{
    test t;

    cout << "t.getValue1() = " << t.getValue1() << endl;	
    cout << "t.getValue2() = " << t.getValue2() << endl;	

    system("pause");
}

编译输出:

从输出我们看到,只有成员变量m_value1被初始化成功了,m_value2是随机值,这是因为我们提前在构造函数中提前返回了,换个比方就是假如构造函数中初始化失败,按目前的方式,也会正常返回,但是其实并没有完成对成员变量的初始化。

    为了解决构造函数中初始化不成功的问题,我下边引出一个概念:二阶构造。大体思路是将构造分为两部分,第一部分为与资源无关的(即不会出现异常),第二部分为可能会出错的(例如访问系统资源、外部文件等等)。

为了实现上述的流程,我们需要上一章节介绍的静态成员函数,代码如下:

#include <iostream>
#include <string>

using namespace std;

class test
{
private:
    int *m_pValue1;
    int *m_pValue2;

    test(){}	//第一阶段,内部可实现一些与资源无关的操作

    bool construct()	//第二阶段,所有可能发生异常的操作都可放在这里执行
    {
    	m_pValue1 = new int(10);
    	m_pValue2 = new int(20);    
   	
    	if (!(m_pValue1 && m_pValue2))	//申请内存失败,则返回false
    	{
    		return false;
    	}
       	return true;
    }

public:
    static test* NewInstance()
    {
    	test* ret = new test();

    	if ( (ret == NULL) || (!ret->construct()) )
    	{
    		delete ret;	 //构造失败或者第二阶段失败,就销毁半成品对象
    		ret = NULL;
    	}

    	return ret;		//否则返回已经生成完成的对象指针
    }

    int getValue1()
    {
    	return *m_pValue1;
    }

    int getValue2()
    {
    	return *m_pValue2;
    }
};

int main()
{
    test *t = test::NewInstance();	//只能使用静态成员函数生成类对象

    if (!t)
    {
    	cout << "Error" << endl;
    }
    else
    {
    	cout << "t.getValue1() = " << t->getValue1() << endl;
    	cout << "t.getValue2() = " << t->getValue2() << endl;
    }

    system("pause");
}

编译输出:

先在我们将第二阶段的bool construct()函数改动一下,使其返回false,模拟访问系统资源出错,这就相当于对象构造失败

bool construct()	//第二阶段,所有可能发生异常的操作都可放在这里执行
{
	m_pValue1 = new int(10);
	m_pValue2 = new int(20);

	return false;

	//if (!(m_pValue1 && m_pValue2))	//申请内存失败,则返回false
	//{
	//	return false;
	//}
	//return true;
}

将改后的代码重新编译输出一下:

这时,我们就知道,构造函数中出错了,就能很快的定位问题点,使用二阶构造模式,使得我们能够知道生成的对象是否完整。

总结:
    -构造函数只能决定对象的初始化状态
    -构造函数中初始化操作的失败不影响对象的诞生
    -初始化不完全的半成品对象是Bug的重要来源
    -二阶构造是人为的把初始化过程分为两部分,确保创建的对象都是完整初始化的。

猜你喜欢

转载自blog.csdn.net/lms1008611/article/details/81411413