关于赋值的疑问?
示例程序:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 int* m_pointer; 9 public: 10 Test() 11 { 12 m_pointer = NULL; 13 } 14 Test(int i) 15 { 16 m_pointer = new int(i); 17 } 18 Test(const Test& obj) 19 { 20 m_pointer = new int(*obj.m_pointer); 21 } 22 Test& operator = (const Test& obj) 23 { 24 if( this != &obj ) 25 { 26 delete m_pointer; 27 m_pointer = new int(*obj.m_pointer); 28 } 29 30 return *this; 31 } 32 void print() 33 { 34 cout << "m_pointer = " << hex << m_pointer << endl; 35 } 36 ~Test() 37 { 38 delete m_pointer; 39 } 40 }; 41 42 int main() 43 { 44 Test t1 = 1; 45 Test t2; 46 47 t2 = t1; 48 49 t1.print(); 50 t2.print(); 51 52 return 0; 53 }
第22行是赋值操作符重载,返回值应该为引用类型,参数也应该为引用类型,否则容易出bug。
24行的判断为了避免自赋值。因为在C语言中,i = i这种自赋值是合法的,C++为了兼容C语言,t2 = t2这种自赋值也是合法的。
我们的数组类将拷贝构造定义为私有的,就是不允许拷贝构造,但是赋值操作应该是要允许的。
改造程序:
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 int& operator [] (int index); 19 IntArray& operator = (const IntArray& obj); 20 IntArray& self(); 21 ~IntArray(); 22 }; 23 24 #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 int& IntArray::operator [] (int index) 72 { 73 return m_pointer[index]; 74 } 75 76 IntArray& IntArray::operator = (const IntArray& obj) 77 { 78 if( this != &obj ) 79 { 80 int* pointer = new int[obj.m_length]; 81 82 if( pointer ) 83 { 84 for(int i=0; i<obj.m_length; i++) 85 { 86 pointer[i] = obj.m_pointer[i]; 87 } 88 89 m_length = obj.m_length; 90 delete[] m_pointer; 91 m_pointer = pointer; 92 } 93 } 94 95 return *this; 96 } 97 98 IntArray& IntArray::self() 99 { 100 return *this; 101 } 102 103 IntArray::~IntArray() 104 { 105 delete[]m_pointer; 106 }
1 #include <iostream> 2 #include <string> 3 #include "IntArray.h" 4 5 using namespace std; 6 7 int main() 8 { 9 IntArray* a = IntArray::NewInstance(5); 10 IntArray* b = IntArray::NewInstance(10); 11 12 if( a && b ) 13 { 14 IntArray& array = a->self(); 15 IntArray& brray = b->self(); 16 17 cout << "array.length() = " << array.length() << endl; 18 cout << "brray.length() = " << brray.length() << endl; 19 20 array = brray; 21 22 cout << "array.length() = " << array.length() << endl; 23 cout << "brray.length() = " << brray.length() << endl; 24 } 25 26 delete a; 27 delete b; 28 29 return 0; 30 }
运行结果如下:
一般性原则:
重载赋值操作符,必然需要实现深拷贝。
编译器默认提供的函数:
关于string的疑问:
我们将s对象转换为char* 指针,然后打印p。然后又将s调用append添加abcde字符,这时我们再打印p指向的字符串,期望输出12345abcde。
上面片段的运行结果如下:
这并不是我们想要的结果。
我们打印s的值是可以得到预期结果的:
问题分析:
调用append之后,s内部的字符指针指向了新的内存空间。 而p还指向原来的空间,这块空间起始已经释放了,因此,p是一个野指针。
我们应该遵循的编程原则是在C++中就采用C++思想,不要混入C语言的思想。不要混合C语言和C++的编程思想。
运行结果如下:
程序并没有打印出12345,不符合我们的预期。
继续试验:
我们可以看到,s的前五个字符确实是12345,但是18行认为s对象还是空的,我们直接使用cout << s << endl打印s的话,也是输出空,这是为什么呢?
问题在于我们用C++来写C语言的程序。
问题分析:
s内部维护了一个表示长度的变量,我们用C语言的方式给s对象赋值,根本不会改变这个长度变量,因此,对象本身认为自己还是空的。
将程序改造如下:
可以成功打印出12345。
切记:不要使用C语言的方式操作C++中的字符串。我们应该直接采用C++面向对象的方式来写代码。
小结: