题目:下面是一个数组类的声明与实现。请分析这个类有什么问题,并针对存在的问题提出几种解决方案。

  1. #include<stdio.h>  
  2. #include <iostream>  
  3. using namespace std;   
  4.    
  5. template<typename T> class Array   
  6. {  
  7.  public:  
  8.      Array(unsigned arraySize):data(0), size(arraySize)  
  9.      {   
  10.          if(size > 0)  
  11.              data = new T[size];   
  12.      }  
  13.      ~Array()   
  14.      {   
  15.          if(data)  
  16.              delete[] data;   
  17.      }   
  18.      void setValue(unsigned index, const T& value)  
  19.      {   
  20.          if(index < size)   
  21.              data[index] = value;   
  22.      }   
  23.      T getValue(unsigned index) const//const的意思是该函数不修改类的数据成员  
  24.      {   
  25.          if(index < size)   
  26.              return data[index];   
  27.          else 
  28.          return T();  
  29.      }   
  30.  private:  
  31.      T* data;   
  32.      unsigned size;  
  33.  
  34.  };  
  35. void main()  
  36. {  
  37. Array<int> A(10); Array<int> B(A);  
  38.  

以上程序执行时会报错,编译器将调用其自动生成的构造拷贝函数或者拷贝运算符的重载函数。在编译器生成的缺省的构造拷贝函数和拷贝运算符的重载函数,对指针实行的是按位拷贝,仅仅只是拷贝指针的地址,而不会拷贝指针的内容。因此在执行完前面的代码之后,A.data和B.data指向的同一地址。当A或者B中任意一个结束其生命周期调用析构函数时,会删除data。由于他们的data指向的是同一个地方,两个实例的data都被删除了。但另外一个实例并不知道它的data已经被删除了,当企图再次用它的data的时候,程序就会不可避免地崩溃。

思路1:我们自己写一个拷贝构造函数来实现深拷贝,并且重载等号运算符

 

  1. #include<stdio.h>  
  2. #include <iostream>  
  3. using namespace std;   
  4.  
  5. template<typename T> class Array   
  6. {  
  7.  public:  
  8.      Array(unsigned arraySize):data(0), size(arraySize)  
  9.      {   
  10.          if(size > 0)  
  11.              data = new T[size];   
  12.      }  
  13.      ~Array()   
  14.      {   
  15.          if(data)  
  16.              delete[] data;   
  17.      }   
  18.      void setValue(unsigned index, const T& value)  
  19.      {   
  20.          if(index < size)   
  21.              data[index] = value;   
  22.      }   
  23.      T getValue(unsigned index) const//const  
  24.      {   
  25.          if(index < size)   
  26.              return data[index];   
  27.          else 
  28.              return T();  
  29.      }   
  30.      Array(const Array& copy):data(0), size(copy.size)   
  31.      {   
  32.          if(size > 0)  
  33.          {  
  34.              data = new T[size];   
  35.              for(int i = 0; i < size; ++ i)   
  36.                  setValue(i, copy.getValue(i));  
  37.          }   
  38.      }  
  39.      const Array& operator = (const Array& copy)   
  40.      {   
  41.          if(this == &copy)  
  42.              return *this;  
  43.          if(data != NULL)  
  44.          {   
  45.              delete []data;   
  46.              data = NULL; }   
  47.          size = copy.size;   
  48.          if(size > 0)   
  49.          {   
  50.              data = new T[size];   
  51.              for(int i = 0; i < size; ++ i)   
  52.                  setValue(i, copy.getValue(i));   
  53.          }   
  54.      }  
  55.  private:  
  56.      T* data;   
  57.      unsigned size;  
  58.        
  59. };  
  60. void main()  
  61. {  
  62.     Array<int> A(10); Array<int> B(A);  
  63.       

思路2:为了防止有多个指针指向的数据被多次删除,我们还可以保存究竟有多少个指针指向该数据。只有当没有任何指针指向该数据的时候才可以被删除。这种思路通常被称之为引用计数技术。在构造函数中,引用计数初始化为1;每当把这个实例赋值给其他实例或者以参数传给其他实例的构造拷贝函数的时候,引用计数加1,因为这意味着又多了一个实例指向它的data;每次需要调用析构函数或者需要把data赋值为其他数据的时候,引用计数要减1,因为这意味着指向它的data的指针少了一个。当引用计数减少到0的时候,data已经没有任何实例指向它了,这个时候就可以安全地删除

  1. #include<stdio.h>    
  2. #include <iostream>    
  3. using namespace std;     
  4. template<typename T> class Array     
  5. {   public:    
  6.         Array(unsigned arraySize) :data(0), size(arraySize), count(new unsigned int)  
  7.         {  
  8.             *count = 1;  
  9.             if(size > 0)  
  10.                 data = new T[size];  
  11.         }  
  12.         ~Array()    
  13.         {    
  14.             Release();  
  15.         }    
  16.         Array(const Array& copy) : size(copy.size), data(copy.data), count(copy.count)  
  17.         {  
  18.             ++ (*count);  
  19.         }  
  20.         const Array& operator = (const Array& copy)   
  21.         {  
  22.             if(data == copy.data) return *this;   
  23.             Release();   
  24.             data = copy.data;   
  25.             size = copy.size;   
  26.             count = copy.count;  
  27.             ++(*count);   
  28.         }  
  29.         private:   
  30.             void Release()   
  31.             {  
  32.                 --(*count);   
  33.                 if(*count == 0)   
  34.                 {   
  35.                     if(data)  
  36.                     {  
  37.                         delete []data;  
  38.                         data = NULL;   
  39.                     }   
  40.                     delete count;  
  41.                     count = 0;  
  42.                 }   
  43.             }   
  44.             unsigned int *count;  
  45.             T* data;     
  46.             unsigned size;      
  47. };   
  48. void main()   
  49. {  
  50.     Array<int> A(10);   
  51.     Array<int> B(A);   
  52. }