我们在前边已经知道,构造函数是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的重要来源
-二阶构造是人为的把初始化过程分为两部分,确保创建的对象都是完整初始化的。