构造函数的回顾:
问题:
异常的构造函数:
第12行我们在构造函数中调用了return,运行结果如下:
mj的值变成了随机数,意味着mj的值没有被初始化。
第28行的创建的对象t1,它虽然执行了构造函数,但是构造函数的内部是有问题的,有问题也仅仅导致了对象的初始状态不正确,并没有影响t1这个对象的诞生。
对象的诞生于构造函数的执行结果是没有任何关系的。
即使构造函数执行不正确,对象依旧会诞生。
我们没有办法判断构造函数产生了什么问题,无法判断构造函数的执行结果。因此,我们没有办法判断创造出来的对象到底能用不能用。
我们强行的在类中设置一个状态变量:
1 #include <stdio.h> 2 3 class Test 4 { 5 int mi; 6 int mj; 7 bool mStatus; 8 public: 9 Test(int i, int j) : mStatus(false) 10 { 11 mi = i; 12 13 return; 14 15 mj = j; 16 17 mStatus = true; 18 } 19 int getI() 20 { 21 return mi; 22 } 23 int getJ() 24 { 25 return mj; 26 } 27 int status() 28 { 29 return mStatus; 30 } 31 }; 32 33 int main() 34 { 35 Test t1(1, 2); 36 37 if( t1.status() ) 38 { 39 printf("t1.mi = %d\n", t1.getI()); 40 printf("t1.mj = %d\n", t1.getJ()); 41 42 } 43 44 return 0; 45 }
可以运行,但是没有任何打印,因为我们的构造函数中调用了return,使得对象构造不完全,在主函数中我们判断状态字,根据状态字得知这个对象不能用。
这个状态字可以认为是我们人为设置的构造函数的返回值。
这确实是一个判断对象是否构造完全的解决方案,但是这不是一个优雅的方案。
应该知道的构造函数的真相:
真相的意义:
构造函数有问题,对象依然能诞生,只是诞生的对象不能用。
半成品对象:
半成品对象是合法的对象,只是它的初始状态不是预期的。
半成品对象的危害:
再来看我们前几节的数组类程序:
第5行中申请内存,如果申请不成功就会导致返回空指针,进一步导致7,8,9行报错。
初步的解决思路是加上空指针判断:
主函数如下:
即使构造函数中申请内存返回了空指针,我们的主函数一样能打印出数组长度为5,但是作为使用者是不知道申请内存失败了的,使用者会认为这是一个合法的对象。
使用者在对这个数组对象进行其他操作时可能就会发生意想不到的错误:
第10行程序导致了段错误。而且从代码的角度看不出任何错误。
二阶构造:
流程:
示例一:
第一阶构造函数是C++里面的构造函数,第二阶构造函数就是一个普通的成员函数。
从上图可以看到,构造函数放到了私有的地方,不能被外界调用。
我们使用对象创建函数供外界调用。
二阶构造示例二:
在使用new创建对象的时候,C++中的第一阶构造函数被自动调用,构造函数是私有的,但是我们在成员函数中使用new创建对象时是可以调用的到的,只是在外界无法直接使用new来创建对象。而第二阶构造函数我们需要手动调用。
当二阶构造返回空指针时,整个对象创建函数就会返回空,这样我们的用户就可以进行一些判断,从而采取一些措施,而不至于使系统重启或者死机。
示例程序:
1 #include <stdio.h> 2 3 class TwoPhaseCons 4 { 5 private: 6 TwoPhaseCons() // 第一阶段构造函数 7 { 8 } 9 bool construct() // 第二阶段构造函数 10 { 11 return true; 12 } 13 public: 14 static TwoPhaseCons* NewInstance(); // 对象创建函数 15 }; 16 17 TwoPhaseCons* TwoPhaseCons::NewInstance() 18 { 19 TwoPhaseCons* ret = new TwoPhaseCons(); 20 21 // 若第二阶段构造失败,返回 NULL 22 if( !(ret && ret->construct()) ) 23 { 24 delete ret; 25 ret = NULL; 26 } 27 28 return ret; 29 } 30 31 32 int main() 33 { 34 TwoPhaseCons* obj = TwoPhaseCons::NewInstance(); 35 36 printf("obj = %p\n", obj); 37 38 delete obj; 39 40 return 0; 41 }
运行结果如下:
数组类的加强:
1 #ifndef _INTARRAY_H_ 2 #define _INTARRAY_H_ 3 4 class IntArray 5 { 6 private: 7 int m_length; 8 int* m_pointer; 9 10 IntArray(int len); 11 IntArray(const IntArray& obj); 12 bool construct(); 13 public: 14 static IntArray* NewInstance(int length); 15 int length(); 16 bool get(int index, int& value); 17 bool set(int index ,int value); 18 ~IntArray(); 19 }; 20 21 #endif
1 #include "IntArray.h" 2 3 IntArray::IntArray(int len) 4 { 5 m_length = len; 6 } 7 8 bool IntArray::construct() 9 { 10 bool ret = true; 11 12 m_pointer = new int[m_length]; 13 14 if( m_pointer ) 15 { 16 for(int i=0; i<m_length; i++) 17 { 18 m_pointer[i] = 0; 19 } 20 } 21 else 22 { 23 ret = false; 24 } 25 26 return ret; 27 } 28 29 IntArray* IntArray::NewInstance(int length) 30 { 31 IntArray* ret = new IntArray(length); 32 33 if( !(ret && ret->construct()) ) 34 { 35 delete ret; 36 ret = 0; 37 } 38 39 return ret; 40 } 41 42 int IntArray::length() 43 { 44 return m_length; 45 } 46 47 bool IntArray::get(int index, int& value) 48 { 49 bool ret = (0 <= index) && (index < length()); 50 51 if( ret ) 52 { 53 value = m_pointer[index]; 54 } 55 56 return ret; 57 } 58 59 bool IntArray::set(int index, int value) 60 { 61 bool ret = (0 <= index) && (index < length()); 62 63 if( ret ) 64 { 65 m_pointer[index] = value; 66 } 67 68 return ret; 69 } 70 71 IntArray::~IntArray() 72 { 73 delete[]m_pointer; 74 }
1 #include <stdio.h> 2 #include "IntArray.h" 3 4 int main() 5 { 6 IntArray* a = IntArray::NewInstance(5); 7 8 printf("a.length = %d\n", a->length()); 9 10 a->set(0, 1); 11 12 for(int i=0; i<a->length(); i++) 13 { 14 int v = 0; 15 16 a->get(i, v); 17 18 printf("a[%d] = %d\n", i, v); 19 } 20 21 delete a; 22 23 return 0; 24 }
运行结果如下:
二阶构造只能在堆上产生对象,不能在栈上产生。工程中恰恰是这样的,工程中对象都比较大,不适合在栈上,一般都在堆上。
小结: