第27课 二阶构造模式(构造函数二阶构造)
1. 构造函数的回顾
(1)关于构造函数
①类的构造函数用于对象的初始化
②构造函数与类同名并且没有返回值(思考:无返回值如何判断构造函数的执行结果?)
③构造函数在对象定义时自动被调用
(2)构造函数的真相
①构造函数只提供自动初始化成员变量的机会,但不能保证初始化逻辑一定成功。它只能决定对象的初始状态,而不是对象的诞生!
②构造函数中执行return语句后,构造函数立即结束
【编程实验】异常的构造函数 27-1.cpp
#include <stdio.h> class Test { private: int mi; int mj; bool mStatus; public: Test(int i, int j):mStatus(false) { mi = i; return;//return,会导致构造函数立即返回 mj = j; mStatus = true; } int getI(){return mi;} int getJ(){return mj;} int status(){return mStatus;} }; int main() { Test t1(1, 2); if(t1.status()) //构造未完成,status为false { printf("t1.mi = %d\n", t1.getI());//i被正确初始化 printf("t1.mj = %d\n", t1.getJ());//j为随机值 } return 0; }
运行结果:
2. 半成品对象
(1)初始化操作不能按照预期完成而得到的对象
(2)半成品对象是合法的C++对象,也是bug的重要来源
3. 二阶构造
(1)工程开发中的构造过程
①资源无关的初始化操作阶段:不会出现异常情况的操作
②需要使用系统资源的操作:可能出现异常,如内存申请,访问文件等。
(2)二阶构造示例
class TwoPhaseCons { private: TwoPhaseCons() // 第一阶段构造函数 { } bool construct() // 第二阶段构造函数 { return true; } public: static TwoPhaseCons* NewInstance(); // 对象创建函数 }; TwoPhaseCons* TwoPhaseCons::NewInstance() { TwoPhaseCons* ret = new TwoPhaseCons(); // 若第二阶段构造失败,返回 NULL if( !(ret && ret->construct()) ) { delete ret; ret = NULL; } return ret; }
【编程实验】二阶构造初探 27-2.cpp
#include <stdio.h> class TwoPhaseCons { private: TwoPhaseCons() // 第一阶段构造函数 { } bool construct() // 第二阶段构造函数 { return true; } public: static TwoPhaseCons* NewInstance(); // 对象创建函数 }; TwoPhaseCons* TwoPhaseCons::NewInstance() { TwoPhaseCons* ret = new TwoPhaseCons(); // 若第二阶段构造失败,返回 NULL if( !(ret && ret->construct()) ) { delete ret; ret = NULL; } return ret; } int main() { TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); printf("obj = %p\n", obj); delete obj; return 0; }
【编程实验】数组类的加强 IntArray
//IntArray.h
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; //将构造函数变为私有的 IntArray(int len); bool construct(); //第2阶构造函数 public: static IntArray* NewInstance(int length);//提供创建对象的函数 int length(); bool get(int index, int& value); bool set(int index, int value); ~IntArray(); }; #endif
//IntArray.cpp
#include "IntArray.h" IntArray::IntArray(int len) { m_length = len; } bool IntArray::construct() { bool ret = true; m_pointer = new int[m_length]; if (m_pointer) { for(int i = 0; i<m_length; i++) { m_pointer[i] = 0; } } else { ret = false; } return ret; } IntArray* IntArray::NewInstance(int length) { IntArray* ret = new IntArray(length); if(!(ret && ret->construct())) { delete ret; ret = 0; } return ret; } IntArray::~IntArray() { if(m_pointer) { delete[] m_pointer; } } int IntArray::length() { return m_length; } bool IntArray::get(int index, int& value) { bool bRet = (0 <= index) && (index <m_length); if(bRet) { value = m_pointer[index]; } return bRet; } bool IntArray::set(int index, int value) { bool bRet = (0 <= index) && (index <m_length); if(bRet) { m_pointer[index] = value; } return bRet; }
//main.cpp
#include <stdio.h> #include "IntArray.h" int main() { IntArray* a = IntArray::NewInstance(5); printf("a.length = %d\n", a->length()); a->set(0, 1); for(int i = 0; i< a->length(); i++) { int v = 0; a->get(i, v); printf("a[%d] = %d\n", i, v); } delete a; return 0; }
4. 小结
(1)构造函数只能决定对象的初始化状态
(2)构造函数中初始化操作的失败不影响对象的诞生
(3)初始化不完全的半成品对象是bug的重要来源
(4)二阶构造人为的将初始化过程分为两个部分
(5)二阶构造能够确保创建的对象都是完整初始化的