程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++箴言:用成員函數模板接受兼容類型

C++箴言:用成員函數模板接受兼容類型

編輯:C++入門知識

 smart pointers(智能指針)是行為很像指針但是增加了指針沒有提供的功能的 objects。例如,《C++箴言:使用對象管理資源》闡述了標准 auto_ptr 和 tr1::shared_ptr 是怎樣被應用於在恰當的時間自動刪除的 heap-based resources(基於堆的資源)的。STL containers 內的 iterators(迭代器)幾乎始終是 smart pointers(智能指針);你絕對不能指望用 "++" 將一個 built-in pointer(內建指針)從一個 linked list(線性鏈表)的一個節點移動到下一個,但是 list::iterators 可以做到。

  real pointers(真正的指針)做得很好的一件事是支持 implicit conversions(隱式轉換)。derived class pointers(派生類指針)隱式轉換到 base class pointers(基類指針),pointers to non-const objects(指向非常量對象的指針)轉換到 pointers to const objects(指向常量對象的指針),等等。例如,考慮在一個 three-level hierarchy(三層繼承體系)中能發生的一些轉換:

class Top { ... };
class Middle: public Top { ... };
class Bottom: public Middle { ... };
Top *pt1 = new Middle; // convert Middle* => Top*
Top *pt2 = new Bottom; // convert Bottom* => Top*
const Top *pct2 = pt1; // convert Top* => const Top*

  在 user-defined smart pointer classes(用戶定義智能指針類)中模仿這些轉換是需要技巧的。我們要讓下面的代碼能夠編譯:

template
class SmartPtr {
 public: // smart pointers are typically
  explicit SmartPtr(T *realPtr); // initialized by built-in pointers
 ...
};

SmartPtr pt1 = // convert SmartPtr =>
SmartPtr (new Middle); // SmartPtr

SmartPtr pt2 = // convert SmartPtr =>
SmartPtr (new Bottom); // SmartPtr

SmartPtr pct2 = pt1; // convert SmartPtr =>
// SmartPtr

  在同一個 template(模板)的不同 instantiations(實例化)之間沒有 inherent relationship(繼承關系),所以編譯器認為 SmartPtr 和 SmartPtr 是完全不同的 classes,並不比(比方說)vector 和 Widget 的關系更近。為了得到我們想要的在 SmartPtr classes 之間的轉換,我們必須顯式地為它們編程。

  在上面的 smart pointer(智能指針)的示例代碼中,每一個語句創建一個新的 smart pointer object(智能指針對象),所以現在我們就集中於我們如何寫 smart pointer constructors(智能指針的構造函數),讓它以我們想要的方式運轉。一個關鍵的事實是我們無法寫出我們需要的全部 constructors(構造函數)。在上面的 hierarchy(繼承體系)中,我們能從一個 SmartPtr 或一個 SmartPtr 構造出一個 SmartPtr ,但是如果將來這個 hierarchy(繼承體系)被擴充,SmartPtr objects 還必須能從其它 smart pointer types(智能指針類型)構造出來。例如,如果我們後來加入

class BelowBottom: public Bottom { ... };
  我們就需要支持從 SmartPtr objects 到 SmartPtr objects 的創建,而且我們當然不希望為了做到這一點而必須改變 SmartPtr template。

  大體上,我們需要的 constructors(構造函數)的數量是無限的。因為一個 template(模板)能被實例化而產生無數個函數,所以好像我們不需要為 SmartPtr 提供一個 constructor function(構造函數函數),我們需要一個 constructor template(構造函數模板)。這樣的 templates(模板)是 member function templates(成員函數模板)(常常被恰如其分地稱為 member templates(成員模板))——生成一個 class 的 member functions(成員函數)的 templates(模板)的范例:

template
class SmartPtr {
 public:
  template // member template
  SmartPtr(const SmartPtr & other); // for a "generalized
  ... // copy constructor"
};

  這就是說對於每一種類型 T 和每一種類型 U,都能從一個 SmartPtr 創建出一個 SmartPtr ,因為 SmartPtr 有一個取得一個 SmartPtr 參數的 constructor(構造函數)。像這樣的 constructor(構造函數)——從一個類型是同一個 template(模板)的不同實例化的 object 創建另一個 object 的 constructor(構造函數)(例如,從一個 SmartPtr 創建一個 SmartPtr )——有時被稱為 generalized copy constructors(泛型化拷貝構造函數)。

  上面的 generalized copy constructor(泛型化拷貝構造函數)沒有被聲明為 explicit(顯式)的。這是故意為之的。built-in pointer types(內建指針類型)之間的類型轉換(例如,從派生類指針到基類指針)是隱式的和不需要 cast(強制轉型)的,所以讓 smart pointers(智能指針)模仿這一行為是合理的。在 templatized constructor(模板化構造函數)中省略 explicit 正好做到這一點。

  作為聲明,SmartPtr 的 generalized copy constructor(泛型化拷貝構造函數)提供的東西比我們想要的還多。是的,我們需要能夠從一個 SmartPtr 創建一個 SmartPtr ,但是我們不需要能夠從一個 SmartPtr 創建一個 SmartPtr ,這就像顛倒 public inheritance(公有繼承)的含義(參見《 C++箴言:確保公開繼承模擬“is-a” 》)。我們也不需要能夠從一個 SmartPtr 創建一個 SmartPtr ,因為這和從 int* 到 double* 的 implicit conversion(隱式轉換)是不相稱的。我們必須設法過濾從這個 member template(成員模板)生成的 member functions(成員函數)的群體。

  假如 SmartPtr 跟隨 auto_ptr 和 tr1::shared_ptr 的腳步,提供一個返回被這個 smart pointer(智能指針)持有的 built-in pointer(內建指針)的拷貝的 get member function(get 成員函數)(參見《 C++箴言:在資源管理類中准備訪問裸資源 》),我們可以用 constructor template(構造函數模板)的實現將轉換限定在我們想要的范圍:

template
class SmartPtr {
 public:
  template
  SmartPtr(const SmartPtr & other) // initialize this held ptr
  : heldPtr(other.get()) { ... } // with others held ptr

  T* get() const { return heldPtr; }
  ...

 private: // built-in pointer held
  T *heldPtr; // by the SmartPtr
};

  我們通過 member initialization list(成員初始化列表),用 SmartPtr 持有的類型為 U* 的指針初始化 SmartPtr 的類型為 T* 的 data member(數據成員)。這只有在“存在一個從一個 U* 指針到一個 T* 指針的 implicit conversion(隱式轉換)”的條件下才能編譯,而這正是我們想要的。最終的效果就是 SmartPtr 現在有一個 generalized copy constructor(泛型化拷貝構造函數),它只有在傳入一個 compatible type(兼容類型)的參數時才能編譯。

  member function templates(成員函數模板)的用途並不限於 constructors(構造函數)。它們的另一個常見的任務是用於支持 assignment(賦值)。例如,TR1 的 shared_ptr(再次參見《 C++箴言:使用對象管理資源 》)支持從所有兼容的 built-in pointers(內建指針),tr1::shared_ptrs,auto_ptrs 和 tr1::weak_ptrs構造,以及從除 tr1::weak_ptrs 以外所有這些賦值。這裡是從 TR1 規范中摘錄出來的一段關於 tr1::shared_ptr 的內容,包括它在聲明 template parameters(模板參數)時使用 class 而不是 typename 的偏好。(就像《 C++箴言:理解typename的兩個含義 》中闡述的,在這裡的上下文環境中,它們的含義嚴格一致。)

template class shared_ptr {
public:
 template // construct from
 explicit shared_ptr(Y * p); // any compatible
 template // built-in pointer,
 shared_ptr(shared_ptr const& r); // shared_ptr,
 template // weak_ptr, or
 explicit shared_ptr(weak_ptr const& r); // auto_ptr
 template
 explicit shared_ptr(auto_ptr & r);
 template // assign from
 shared_ptr& operator=(shared_ptr const& r); // any compatible
 template // shared_ptr or
 shared_ptr& operator=(auto_ptr & r); // auto_ptr
 ...
};

  除了 generalized copy constructor(泛型化拷貝構造函數),所有這些 constructors(構造函數)都是 explicit(顯式)的。這就意味著從 shared_ptr 的一種類型到另一種的 implicit conversion(隱式轉換)是被允許的,但是從一個 built-in pointer(內建指針)或其 smart pointer type(智能指針類型)的 implicit conversion(隱式轉換)是不被許可的。(explicit conversion(顯式轉換)——例如,經由一個 cast(強制轉型)——還是可以的。)同樣引起注意的是 auto_ptrs 被傳送給 tr1::shared_ptr 的 constructors(構造函數)和 assignment operators(

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved