第36课 经典问题解析三

关于赋值的疑问?

示例程序:

 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++面向对象的方式来写代码。

小结:

原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9573218.html