本文内容取自于对狄泰学院 唐佐林老师 C++深度解析 课程的学习总结
构造函数的回顾
关于构造函数
- 类的 构造函数 用于对象的 初始化
- 构造函数 与类同名并且没有返回值
- 构造函数在对象定义时 自动被调用
问题
- 如判断 构造函数 的执行结果?
- 在构造函数中执行 return 语句会发生什么?
- 构造函数执行 结束是否意味着 对象构造成功?
为什么了回答这几个问题,我们编写一个构造函数,并且构造函数中添加 return 语句
#include <stdio.h>
class Test
{
private:
int m_i;
int m_j;
public:
Test(int i, int j);
int getI();
int getJ();
};
Test::Test(int i, int j)
: m_i(0),m_j(0)
{
m_i = i;
/* 在m_j没有赋值的情况下强制返回 */
return;
m_j = j;
}
int Test::getI()
{
return m_i;
}
int Test::getJ()
{
return m_j;
}
int main(void)
{
Test t1(1, 2);
printf("t1.m_i = %d\n", t1.getI());
printf("t1.m_j = %d\n", t1.getJ());
return 0;
}
执行结果
程序第27行给构造函数使用 return 强制返回,结果是 构造 t1 对象时,t1的m_j成员变量并没有成功赋值为2,依然为初始化时的0。 m_i 成功赋值。
所以 t1 对象并没有完全构造成功,而是一个 半成品对象 。
实验结论
构造函数
- 只提供 自动初始化成员变量的机会
- 不能保证 初始化逻辑一定成功
- 执行 return 语句后 构造函数立即结束
构造函数能决定的 只是对象的初始状态
而不是对象的诞生!!!
半成品对象
半成品对象 的概念
- 初始化操作不能按照预期完成而得到的对象
- 半成品对象是 合法的C++对象,也是 Bug 的重要来源
为了说明半成品对象的危害,我们来做个实验
首先实现一个数组类,实验构造数组长度,设置数组元素值,获取数据元素值,获取数组长度功能。
Array.h
#ifndef __ARRAY_H__
#define __ARRAY_H__
class Array{
private:
int m_len;
int *m_pointer;
public:
Array(int len);
bool setArrayVal(int index, int val);
bool getArrayVal(int index, int& val);
int getArrayLen();
};
#endif
Array.cpp
#include "Array.h"
Array::Array(int len)
{
m_pointer = new int[len];
for(int i=0; i<len; i++)
m_pointer[i] = 0;
m_len = len;
}
bool Array::setArrayVal(int index, int val)
{
bool ret = (index >=0) && (index < m_len);
if(ret)
m_pointer[index] = val;
return ret;
}
bool Array::getArrayVal(int index, int& val)
{
bool ret = (index >= 0) && (index < m_len);
if(ret)
val = m_pointer[index];
return ret;
}
int Array::getArrayLen()
{
return m_len;
}
main.cpp
#include "Array.h"
#include <stdio.h>
int main(void)
{
Array a(5);
for(int i=0; i<a.getArrayLen(); i++)
{
a.setArrayVal(i, i+1);
}
for(int i=0; i<a.getArrayLen(); i++)
{
int val = 0;
a.getArrayVal(i, val);
printf("%d : %d\n", i, val);
}
return 0;
}
运行结果:
实验结果:成功的实现了我们预期的结果,构造了一个长度为5的数组对象,并对该对象元素进行赋值。
现在,我们来人为制造半成品对象,假设构造对象时构造函数中 申请数组堆内存失败,会发生什么情况
运行结果
实验结论:由于堆内存申请失败,设置数组元素值时操作空指针导致程序段错误
因为构造函数没有返回值,我们无法获知堆内存申请的结果,为了避免这种情况
我们增加了二阶构造模式
二阶构造
工程开发中的 构造过程 可分为
1.资源无关 的初始化操作
不可能出现异常情况 的操作
2. 需要 使用系统资源 的操作
可能出现异常情况,如:内存申请,访问文件等
二阶构造过程
二阶构造示例
对象创建函数示例
下面,我们将刚才的代码做二阶构造改写
1.在Array.h中增加二阶构造接口
在Array.cpp中实现二阶构造接口
在main.cpp中调用对象创建函数进行对象的创建
运行结果
利用 二阶构造模式 可以对对象创建结果进行判断,避免生成半成品对象而导致程序异常的发生
小结
- 构造函数 只能决定对象的初始化状态
- 构造函数中 初始化操作的失败不影响对象的诞生
- 初始化不完全的 半成品对象是 Bug 的重要来源
- 二阶构造人为的 将初始化分为两部分
- 二阶构造 能够确保创建的 对象都 是完整初始化的