构造函数:
- 类的构造函数用于对象的初始化
- 构造函数与类同名并且没有返回值
- 构造函数在对象定义时自动被调用
提出问题:
1.如何判断构造函数的执行结果?
2.在构造函数中执行return语句会发生什么?
3.构造函数执行结束是否意味着对象构造成功?
异常的构造函数:
#include <iostream>
using namespace std;
class Test
{
int mi;
int mj;
public:
Test(int i, int j)
{
mi = i;
return;//编译通过证明合法,执行了return语句,后面的语句不会再执行
mj = j;
}
int getI()
{
return mi;
}
int getJ()
{
return mj;
}
};
int main()
{
Test t1(1, 2); //对象成功诞生了,虽然构造函数出了问题
cout<<"t1.mi = "<<t1.getI()<<endl;//打印1
cout<<"t1.mj = "<<t1.getJ()<<endl;//打印随机值
return 0;
}
问题答案:
1.没有方法判断构造函数的执行结果
2.构造函数可以有return语句,执行return语句后立即返回,构造函数结束
3.构造函数的结束并不意味着对象创建的完成
解决方法一:增加一个状态判断的变量mStatus
#include <iostream>
using namespace std;
class Test
{
int mi;
int mj;
bool mStatus;
public:
Test(int i, int j) : mStatus(false)//先初始化为false
{
mi = i;
// return;
mj = j;
mStatus = true;//在构造函数结束的位置设置为true
}
int getI()
{
return mi;
}
int getJ()
{
return mj;
}
int status()
{
return mStatus;
}
};
int main()
{
Test t1(1, 2);
if( t1.status() )
{
cout<<"t1.mi = "<<t1.getI()<<endl;
cout<<"t1.mj = "<<t1.getJ()<<endl;
}
return 0;
}
构造函数的补充:
1.只提供自动初始化成员变量的机会
2.不能保证初始化逻辑一定成功
3.执行return语句后构造函数立即结束
真相的意义:
构造函数能决定的只是对象的初始状态,而不是对象的诞生。
即使构造函数没有执行完全也不影响对象的诞生,只是诞生的对象无法使用,
这种对象叫做半成品对象。
半成品对象:
初始化操作不能按照预期完成而得到的对象
半成品对象是合法的C++对象,也是bug的重要来源
示例:
数组类里面,构造函数中申请内存空间,但不一定能申请成功
Array::Array(int length)
{
m_length = length;
/*这里申请了内存空间很有可能不成功,得到m_poiter = 0;就会产生半成品对象;
但也不会每次都不成功,所以也不可能每次都产生半成品对象*/
m_poiter = new int[m_length];
if(m_poiter)
{
for(int i = 0; i < m_length; i++)
{
m_poiter[i] = 0;
}
m_length = len;
}
}
在类的调用中使用:
int main()
{
Array array(5);
cout << array.get_length()<<endl;//在这里肯定会打印5,自然认为对象已正确构建
for(int i = 0; i < array.get_length(); i++)
{
/*但实际时内存没有申请成功,这里就会产生段错误,
而当内存成功申请的时候这里又可以正常运行,这种bug非常难以调试*/
array.set_data(i, i+1);
}
}
真正的解决方案:
二阶构造
相关定义:
工程开发中的构造过程可分为:
- 资源无关的初始化操作 : 不可能出现异常情况的操作
- 需要使用系统资源的操作: 可能出现异常情况,如:内存申请,访问文件
工程开发中的构造过程可分为:
1.资源无关的初始化操作(不可能出现异常情况的操作)
2.申请系统资源的操作
3.两个构造函数放在私有位置,外界不能直接访问,因此需要定义一个public的对象创建函数
#include <iostream>
using namespace std;
class TwoPhaseCons
{
private:
TwoPhaseCons() // 第一阶段构造函数
{
}
bool construct() // 第二阶段构造函数
{
return true;
}
public:
static TwoPhaseCons* NewInstance(); // 对象创建函数,定义成static的原因是可以直接通过类名来访问
};
TwoPhaseCons* TwoPhaseCons::NewInstance()
{
TwoPhaseCons* ret = new TwoPhaseCons();
// 若第二阶段构造失败,返回 NULL
if( !(ret && ret->construct()) )
{
delete ret;
ret = NULL;
}
return ret;
}
int main()
{
TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); //静态成员函数
cout<<"obj = "<<obj<<endl;
delete obj;
return 0;
}
改写数组类:
arrar.h:
#ifndef _ARRAY_H_
#define _ARRAY_H_
#include<iostream>
class Array
{
private:
int m_length;
int *m_poiter;
/*先将构造函数移位,然后添加第二阶构造的函数*/
Array(int length);
Array(const Array& obj); //使用二阶构造,生成的对象都在堆空间里面,不需要拷贝构造函数
bool construct();
public:
int get_length();
bool set_data(int index, int data);
bool get_data(int index, int& data);
//对象创建函数
static Array* NewInsrance(int length);
~Array();
};
#endif
array.cpp:
#include"array.h"
Array::Array(int length)
{
/*第一阶段构造做一些不可能产生异常的操作*/
m_length = length;
}
/*第二阶段构造的实现*/
bool Array::construct()
{
bool ret = true;
m_poiter = new int[m_length];
if(m_poiter)
{
for(int i = 0; i < m_length; i++)
{
m_poiter[i] = 0;
}
}
else
{
ret = false;
}
return ret;
}
//对象创建函数
Array* Array::NewInsrance(int length)
{
Array* ret = new Array(length);
if(!(ret && ret->construct()))
{
delete ret;
ret = NULL;
}
return ret;
}
int Array::get_length()
{
return m_length;
}
bool Array::set_data(int index, int data)
{
bool ret = ((0 <= index)&&(index<get_length()));
if(ret)
{
m_poiter[index] = data;
}
return ret;
}
bool Array::get_data(int index, int& data)
{
bool ret = ((0<=index) && (index<get_length()));
if(ret)
{
data = m_poiter[index];
}
return ret;
}
Array::~Array()
{
delete[] m_poiter;
}
main.cpp:
#include"array.h"
using namespace std;
int main()
{
Array* array = Array::NewInsrance(5);
cout << array->get_length()<<endl;
cout << endl;
for(int i = 0; i < array->get_length(); i++)
{
array->set_data(i, i+1);
}
for(int i = 0; i < array->get_length(); i++)
{
int data = 0;
if(array->get_data(i, data))
{
cout << data << endl;
}
}
delete array;
return 0;
}
小结:
1.构造函数只能决定对象的初始化状态
2.构造函数中初始化操作的失败不影响对象的产生
3.初始化不完全的半成品对象是bug的重要来源
4.二阶构造函数人为将初始化过程分为两部分
5.二阶构造能够确保创建的对象都是完整初始化的