程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> More Effective C++:不使用多態性數組

More Effective C++:不使用多態性數組

編輯:C++入門知識

  類繼續的最重要的特性是你可以通過基類指針或引用來操作派生類。這樣的指針或引用具有行為的多態性,就似乎它們同時具有多種形態。C++答應你通過基類指針和引用來操作派生類數組。不過這根本就不是一個特性,因為這樣的代碼根本無法如你所願地那樣運行。
  
  假設你有一個類BST(比如是搜索樹對象)和繼續自BST類的派生類BalancedBST:
  
   class BST { ... };
  class BalancedBST: public BST { ... };
  在一個真實的程序裡,這樣的類應該是模板類,但是在這個例子裡並不重要,加上模板只會使得代碼更難閱讀。為了便於討論,我們假設BST和BalancedBST只包含int類型數據。
  
  有這樣一個函數,它能打印出BST類數組中每一個BST對象的內容:
  
   void printBSTArray(ostream& s,
  const BST array[],
  int numElements)
  {
   for (int i = 0; i < numElements; ) {
  s << array[i]; //假設BST類
   } //重載了操作符<<
  }
  當你傳遞給該函數一個含有BST對象的數組變量時,它能夠正常運行:
  
   BST BSTArray[10];
  ...
  printBSTArray(cout, BSTArray, 10); // 運行正常
  然而,請考慮一下,當你把含有BalancedBST對象的數組變量傳遞給printBSTArray函數時,會產生什麼樣的後果:
  
   BalancedBST bBSTArray[10];
  ...
  printBSTArray(cout, bBSTArray, 10); // 還會運行正常麼?
  你的編譯器將會毫無警告地編譯這個函數,但是再看一下這個函數的循環代碼:
  
   for (int i = 0; i < numElements; ) {
   s << array[i];
  }
  這裡的array[I]只是一個指針算法的縮寫:它所代表的是*(array)。我們知道array是一個指向數組起始地址的指針,但是array中各元素內存地址與數組的起始地址的間隔究竟有多大呢?它們的間隔是i*sizeof(一個在數組裡的對象),因為在array數組[0]到[I]間有I個對象。編譯器為了建立正確遍歷數組的執行代碼,它必須能夠確定數組中對象的大小,這對編譯器來說是很輕易做到的。參數array被聲明為BST類型,所以array數組中每一個元素都是BST類型,因此每個元素與數組起始地址的間隔是be i*sizeof(BST)。
  
  至少你的編譯器是這麼認為的。但是假如你把一個含有BalancedBST對象的數組變量傳遞給printBSTArray函數,你的編譯器就會犯錯誤。在這種情況下,編譯器原先已經假設數組中元素與BST對象的大小一致,但是現在數組中每一個對象大小卻與BalancedBST一致。派生類的長度通常都比基類要長。我們料想BalancedBST對象長度的比BST長。假如如此的話,printBSTArray函數生成的指針算法將是錯誤的,沒有人知道假如用BalancedBST數組來執行printBSTArray函數將會發生什麼樣的後果。不論是什麼後果都是令人不愉快的。
  
  假如你試圖刪除一個含有派生類對象的數組,將會發生各種各樣的問題。以下是一種你可能的不正確的做法。
  
   //刪除一個數組, 但是首先記錄一個刪除信息
  void deleteArray(ostream& logStream, BST array[])
  {
   logStream << "Deleting array at address "
   << static_cast(array) << ' ';
   delete [] array;
  }
  BalancedBST *balTreeArray = // 建立一個BalancedBST對象數組
  new BalancedBST[50];
  ...
  deleteArray(cout, balTreeArray); // 記錄這個刪除操作
  這裡面也掩藏著你看不到的指針算法。當一個數組被刪除時,每一個數組元素的析構函數也會被調用。當編譯器碰到這樣的代碼:
  
   delete [] array;
  它肯定象這樣生成代碼:
  
   // 以與構造順序相反的順序來
  // 解構array數組裡的對象
  for ( int i = 數組元素的個數 1; i >= 0;--i)
  {
   array[i].BST::~BST(); // 調用 array[i]的
  } // 析構函數
  因為你所編寫的循環語句根本不能正運行,所以當編譯成可執行代碼後,也不可能正常運行。語言規范中說通過一個基類指針來刪除一個含有派生類對象的數組,結果將是不確定的。這實際意味著執行這樣的代碼肯定不會有什麼好結果。多態和指針算法不能混合在一起來用,所以數組與多態也不能用在一起。
  
  值得注重的是假如你不從一個具體類(concrete classes)(例如BST)派生出另一個具體類(例如BalancedBST),那麼你就不太可能犯這種使用多態性數組的錯誤。正如我在後面將介紹的條款33所解釋的,不從具體類派生出具體類有很多好處。
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved