2010-08-15 109 views
0

編輯: 請注意,我在這裏最終目的不是具有一流的工作,僅僅是學習更多有關模板:-)嵌套模板和構造

假設你有一個實現載體的模板類:

template <typename T> 
class Vector 
{ 
    public: 
     Vector(size_t dim) { 
      dimension = dim; 
      elements = new T[dim]; 
     } 
     /* Here more stuff, like operator[] etc... */ 
    private: 
     size_t dimension; 
     T * elements; 
} 

並且假設你想用它構建一個矩陣。矩陣僅僅是向量的載體,因此可以設計如下:

template <typename T> 
class Matrix : public Vector<Vector<T> > 
{ 
    /*...*/ 
} 

這裏來麻煩:在構造函數中我需要提供行和列作爲參數傳遞給內部的載體。它應該是這樣的

template <typename T> 
Matrix<T>::Matrix (size_t ncols, size_t nrows) 
     : Vector<Vector<T> > /* Here I need to specify size for both 
           * internal and external vectors */ 
{ 
} 

很明顯,我不能寫Vector<Vector<T>(nrows)>(ncols),但這就是我所需要的!

一個可能的解決方案將包括在模板中大小:

template <typename T, size_t N> 
class Vector 
{ 
    public: 
     Vector() { 
      elements = new T[N]; 
     } 
     /* Here more stuff, like operator[] etc... */ 
    private: 
     T * elements; 
} 

因此,我將不再需要構造函數的參數,但是這也迫使我寫的是一個到處模板(由exmample笨拙的代碼,每個函數使用一個Vector應被聲明爲

template <typename T, size_t N> 
void foo (Vector<T,N> &vec) {...} 

你有更好的辦法呢?

編輯:

作爲解決方案,我從Fooz先生和chubsdad的帖子中獲得靈感。這就是我解決問題的方法:

/* The RowAccess class is just an array wrapper which raises an exception 
* if you go out of bounds */ 
template <typename T> 
class RowAccess 
{ 
    public: 

     RowAccess (T * values, unsigned cols) : values(vals), cols(c) {} 

     T & operator[] (unsigned i) throw (MatrixError) { 
      if (i < cols) return values[i]; 
      else throw MatrixError("Column out of bound"); 
     } 

    private: 
     T * values; 
     unsigned cols; 
}; 

template <typename T> 
class Matrix 
{ 
    public: 
     Matrix (unsigned rows, unsigned cols) {...} 
     virtual ~Matrix() {...} 

     RowAccess<T> operator[] (unsigned row) { 
      if (row < rows) return RowAccess<T>(values + cols * row, cols); 
      else throw MatrixError("Row out of boundary"); 
     } 

    private: 
     unsigned rows; 
     unsigned cols; 
     T * values; 
}; 

非常感謝大家!

+0

爲什麼'foo'「笨拙」?即使沒有N參數,它仍然會被模板化。 – 2010-08-15 12:05:07

+0

'size_t N'和運行時'dim'值解決了不同的問題。如果大小在編譯時是恆定的並且已知的,那麼你應該使用'size_t N'的方式,因爲它使用了類型檢查。然後在編譯時診斷可能的溢出。在這種情況下,根本不需要'new' - 只需放入一個普通的數組,像'boost :: array '。 – 2010-08-15 12:27:27

+0

@jon hanson:是的,但是類型比值更普遍,因此定義了'foo',因此定義了'foo',它比'foo'更好(第 &vec);'。對於第二個,我傾向於將foo定義爲模板函數。 – Dacav 2010-08-15 12:41:26

回答

1

這不是你問的問題,但是矩陣可能更好地實現爲單個線性向量,您可以在其中提供執行索引的高級訪問方法(例如elmLoc = row * ncols + col )。這樣你就不需要創建和初始化矢量矢量。你也不需要擔心不小心有一些不同大小的內部向量。我曾經使用的所有密集矩陣實現都使用單個線性向量作爲底層實現。

+0

好的建議,請參閱答案更新。 – Dacav 2010-08-15 20:43:24

0

這取決於你對Vector(和Matrix)類的期望。

要麼你想在運行時確定大小,在這種情況下,我會建議添加一個resize()函數,這將允許你在構造函數中調整Vector的大小。

template <typename T> 
class Vector 
{ 
    public: 
     Vector(size_t dim) { 
      dimension = dim; 
      elements = new T[dim]; 
     } 
     Vector() : dimension(0), elements(0) {} // you need default constructor 
     void resize(size_t dim) { // note: this could be implemented a lot better 
      T* new_elements=new T[dim]; 
      for(int i=0; i<dim && i<dimension; i++) 
      new_elements[i]=elements[i]; 
      delete [] elements; 
      elements=new_elements; dimension=dim; 
     } 
     /* Here more stuff, like operator[] etc... */ 
    private: 
     size_t dimension; 
     T * elements; 
} 

然後你會調整你的Vectors在一個循環中Matrix構造。

如果你想在編譯時確定你的向量或矩陣的大小,最好的辦法可能是按照你的建議使用模板非類型參數。

+0

這可能是一個很好的解決方案,我也談過它,問題是它允許用戶創建一個空向量/矩陣,從而使庫變得更弱,並且更容易發生錯誤 – Dacav 2010-08-15 12:14:05

2

使用投放新的這樣的(埋在uninitialized_fill調用後面)

template <typename T> 
class Vector 
{ 
    public: 
     Vector(size_t dim, T const& c = T()) { 
      dimension = dim; 
      elements = 
       static_cast<T*>(operator new(sizeof(T) * dim)); 
      std::uninitialized_fill(elements, elements + dim, c); 
     } 
     /* Here more stuff, like operator[] etc... */ 
    private: 
     size_t dimension; 
     T * elements; 
}; 

然後就可以調用與Matrix::Vector(ncols, Vector<T>(nrows))構造函數(你不需要重複的外部矢量的說法,因爲Vector指因爲你從外部向量繼承了,所以你需要確保在析構函數中執行operator delete(elements)之前手動調用析構函數。

您可能還想將矢量作爲成員嵌入,我可能會更喜歡此矢量,因爲我想象不一定所有外部矢量的操作對矩陣都有意義。然後初始化看起來像m(ncols, Vector<T>(nrows))


應當指出的是,std::vector也可用於本

template <typename T> 
class Vector 
{ 
    public: 
     Vector(size_t dim, T const& c = T()):elements(dim, c) 
     { } 
    private: 
     std::vector<T> elements; 
}; 

這是實現,一個簡單而安全的方式,你會得到自動內存管理。

+0

基本上,你正在使用'operator new sizeof(T)* dim)'而不是'new T [dim]'以避免構造函數被調用? – Dacav 2010-08-15 12:26:48

+0

...另外,將超構造函數稱爲Matrix :: Vector(...)也是合法的。 )'' – Dacav 2010-08-15 12:30:09

+0

@Dacav你需要這樣調用它,如果你只是說'Vector(...)',那麼這個名稱將不會在基類中查找,因爲它取決於一個模板參數。完全合法的以您認爲合適的方式命名基類的類型。這裏不需要'typename',因爲就像使用基類子句的類名一樣,這意味着你正在命名一個類型。 – 2010-08-15 12:32:14

3

從OO的角度來說,我會投票表決Matrix和vector之間有「有」關係。矩陣有向量,而不是矩陣「是」向量,這意味着矩陣不應該從向量派生。

編輯1:一個小的更正。 「這意味着Matrix不應該從Vector中」公開「。私人繼承可能仍然很好。

+0

這確實是一個好主意(簡單很好!),但需要我重新定義兩次類似的方法(如運算符[]) – Dacav 2010-08-15 12:36:12

+1

@Dacav:繼承和組合不是一回事,繼承不是代碼-使用。因爲它沒有虛擬析構函數,所以允許隱式轉換爲矢量>,以打開自己的未定義行爲。當你必須爲Matrix類定義你自己的operator []時,它將它轉換爲底層向量。 – Puppy 2010-08-15 12:44:53

+0

@DeadMG:嗯,這是一個明智的觀點(+1在你的評論)。然而,我正試圖學習如何處理這種情況(即我在問題中描述的那種情況),這種情況與繼承有關。 – Dacav 2010-08-15 12:57:09