C#、Java都沒有Copy Control,為什麼C++需要有Copy Control呢?

C++是個Hybrid語言,除了built-in type和Class type外,還有個其他語言都沒有的歷史產物:pointer,pointer的用途很多,其中一個用途是因為Dynamic Allocation,而且這種由Dynamic Allocation產生的pointer有幾個特點,第一就是他存的是Memory Address不是Data,所以Copy Constructor和Assignment Operator會有問題,第二就是須delete才會消失,不會隨著object out of scope而消失,有static的味道,所以必須自己在Destructor處理。C#、Java因為沒有pointer,因此不需手動處理Copy Constructor和Assignment Operator,但C#、Java雖有Garbage Collection,但C#仍有Destructor,主要是為了處理非Managed的Resource,如File Handler,Database Connection,但Java已經沒有Destructor了。

回到主題,C++的Copy Contructor和Assignment Operator有什麼問題呢?由Compiler所synthesized的程式,只會將pointer加以複製,很顯然最後指向的結果仍是同一份,這樣就沒有達到Copy的意義了,所以我們得自己重新定義Copy Constructor和Assignment Operator。至於Destructor,因為須手動delete,所以Destructor也必須重新定義。換言之,只要Data Member有用到pointer,也就是動態資料結構時,Copy Constructor、Assignment Operator、Destructor就必須重新定義。

以下範例Demo如何撰寫Copy Control處理單一動態資料和動態陣列。

  1  /**//* 

  2  (C) OOMusou 2007  

  3  

  4  Filename    : CopyControl.cpp

  5  Compiler    : Visual C++ 8.0 / ISO C++

  6  Description : Demo how to use Copy Control with dynamic allocation

  7  Release     : 01/15/2007 1.0

  8  */

  9 #include <iostream>

 10 

 11 using namespace std;

 12  

 13    class Foo  {

 14 // Constructor

 15 public: 

 16    Foo(int n = 0) : i(n), pi(new int(n)), pia(new int[n])  { cout << "Constructor" << endl; }

 17 

 18 public:

 19   // Copy Constructor

 20    Foo (const Foo&);

 21   // Assignment Operator

 22   Foo& operator=(const Foo&);

 23   // Destructor

 24   ~Foo();

 25 

 26 public:

 27   int getStaticInt();

 28   int getDynamicInt();

 29   int getDynamicArray(int);

 30   void setDynamicArray(int, int);

 31 

 32 private:

 33   int i; // static int

 34   int *pi; // dynamic int

 35   int *pia; // dynamic int array

 36 };

 37 

 38 // Synthesized Copy Constructor 

 39  /**//*

 40 Foo::Foo(const Foo& foo) {

 41   this->i = foo.i;

 42   this->pi = foo.pi;   // Error!! just copy pointer

 43    this->pia = foo.pia; // Error!! just copy pointer

 44 }

 45 */

 46 

 47 // Correct Copy Constructor

 48  Foo::Foo (const Foo& foo)  {

 49   this->i = foo.i;

 50 

 51   this->pi = new int(*foo.pi);

 52 

 53   this->pia = new int[foo.i];

 54   for(int n = 0; n != this->i; ++n)

 55     this->pia[n] = foo.pia[n];

 56  

 57   cout << "Copy Constructor" << endl;

 58 }

 59 

 60 // Synthesized Assignment Operator

 61    /**//*

 62 Foo& Foo::operator=(const Foo& foo) {

 63   this->pstring = foo.pstring; // error!! just copy pointer

 64   this->i = 0;

 65   this->d = 0.0;

 66   return *this; 

 67 }

 68 */

 69 

 70 // Correct Assignment Operator

 71  Foo& Foo::operator=(const Foo& foo)  {

 72   this->i = foo.i;

 73  

 74   *this->pi = *foo.pi;

 75 

 76    /**//*while(this->pia < this->pia + foo.i)

 77     *(this->pia++) = *(foo.pia++);*/

 78 

 79    for(int n = 0; n != this->i; ++n)

 80     this->pia[n] = foo.pia[n];

 81 

 82   cout << "Assign Operator" << endl;

 83 

 84   return *this; 

 85 }

 86 

 87 // Destructor

 88  Foo::~Foo()  {

 89   // delete dynamic int

 90    delete this->pi; 

 91   // delete dynamic int array

 92   delete [] this->pia;

 93 

 94   cout << "Destructor" << endl;

 95 }

 96 

 97  int Foo::getStaticInt()  {

 98   return this->i;

 99 }

100 

101  int Foo::getDynamicInt()  {

102   return *this->pi;

103 }

104   

105  int Foo::getDynamicArray(int n)  {

106   return this->pia[n];

107 }

108 

109  void Foo::setDynamicArray(int n, int val)  {

110   if (n >= 0 && n < this->i) 

111     this->pia[n] = val;

112 }

113 

114  int main()  {

115   Foo foo1;

116    cout << foo1.getStaticInt() << endl;

117   cout << foo1.getDynamicInt() << endl;

118 

119   Foo foo2(3);

120   cout << foo2.getStaticInt() << endl;

121   cout << foo2.getDynamicInt() << endl;

122 

123   for(int n = 0; n != 3; ++n) 

124     foo2.setDynamicArray(n, n+1);

125 

126   cout << foo2.getDynamicArray(0) << endl;

127 

128    Foo foo3(foo2);  // foo3's Copy Constructor

129   Foo foo4 = foo2; // Syntax sugar!! foo4's Copy Constructor

130 

131   Foo foo5(3);

132   foo5 = foo3;     // foo5's Assignment Operator

133   cout << foo5.getDynamicArray(1) << endl;

134 }

執行結果

 Constructor

 0

 0

 Constructor

 3

  3

  1

  Copy Constructor

 Copy Constructor

 Constructor

 Assign Operator

 

 Destructor

  Destructor

 Destructor

  Destructor

 Destructor