2011-04-12 46 views
0

說我需要一個新的類型在我的應用程序,它由一個功能擴展的std::vector<int>組成。直截了當的方法是組成(由於STL容器的繼承限制):通過組合或免費功能擴展STL容器?

class A { 
    public: 
     A(std::vector<int> & vec) : vec_(vec) {} 
     int hash(); 
    private: 
     std::vector<int> vec_ 
} 

這需要用戶首先構建一個vector<int>並在構造函數的副本,這是當我們要應付一個壞相當大數量的大型矢量。當然,可以寫一個傳遞到push_back(),但是這引入了可變狀態,我想避免這種狀態。

因此,在我看來,我們可以避免複製或保持不變,這是正確的嗎?

如果是的話,最簡單的(和效率明智當量)的方法是使用typedef和free函數在命名空間範圍:

namespace N { 
typedef std::vector<int> A; 
int a_hash(const A & a); 
} 

這只是感覺不對莫名其妙,因爲在未來將擴展「污染「命名空間。此外,在任何vector<int>調用a_hash(...)是可能的,這可能會導致意想不到的結果(假設我們施加約束上的一個用戶必須遵循或否則將在第一個例子執行)

我的兩個問題是:

  • 在使用上面的類代碼時,如何不犧牲不變性和效率?
  • 什麼時候使用自由函數而不是封裝在類/結構中有意義?

謝謝!

+0

你在談論什麼「STL容器的繼承限制」?當然,它們不能以多態的方式派生,但是當你用包裝器確定時,你不需要這樣做。也就是說,如果你希望得到的對象是不可變的(當基類不是)時,繼承不是正確的工具。 – 2011-04-12 05:53:58

+0

相關問題:http://stackoverflow.com/questions/679520/advice-on-a-better-way-to-extend-c-stl-container-with-user-defined-methods – 2011-04-12 06:01:17

回答

6

散列算法不是一種類型,也可能不應該限制爲任何特定容器類型的數據。如果你想提供哈希,最好創建一個函數來計算哈希一次元素(int,正如你以前寫過的東西),然後用std::accumulatestd::for_each將它應用到集合:

namespace whatever { 
struct hasher { 
    int current_hash; 
public: 
    hasher() : current_hash(0x1234) {} 

    // incredibly simplistic hash: just XOR the values together. 
    operator()(int new_val) { current_hash ^= new_val; } 
    operator int() { return current_hash; } 
}; 
} 

int hash = std::for_each(coll.begin(), coll.end(), whatever::hasher()); 

注意,這樣使得coll是一個vector,或者deque,或者你可以用一對istream_iterators文件中的散列數據...

0

一個簡單的解決方案是聲明私有成員變量作爲參考 &在構造函數中初始化。這種方法引入了一些限制,但在大多數情況下這是一個很好的選擇。

class A { 
    public: 
     A(std::vector<int> & vec) : vec_(vec) {} 
     int hash(); 
    private: 
     std::vector<int> &vec_; // 'vec_' now a reference, so will be same scoped as 'vec' 
}; 
+2

這是一個參考你無法控制的對象。糟糕的主意。 – 2011-04-12 06:13:03

+0

這就是爲什麼我提到它會引入限制,因爲它與原始對象的範圍相同。 – iammilind 2011-04-12 06:28:45

+0

這是一個巨大的限制。這是非常意想不到的行爲,有人會錯過任何你提供的有關主題的巨大警告,並且在不存儲'vec'的情況下存儲'A'並運行以防止未定義的行爲。除非在特定情況下,不要這樣做,我不會將其歸類爲其中一種。 – 2011-04-12 06:45:26

0

廣告一成不變:您可以使用向量的範圍構造函數並創建輸入迭代器以提供內容爲矢量。範圍構造函數只是:

template <typename I> 
A::A(I const &begin, I const &end) : vec_(begin, end) {} 

生成器有點棘手。如果您現在有一個使用push_back構造向量​​的循環,則需要進行相當多的重寫操作才能轉換爲一次從方法返回一個項目的對象。比你需要在一個有效的輸入迭代器中引用它。

廣告免費功能:由於重載,污染命名空間通常不是問題,因爲符號只會被視爲具有特定參數類型的調用。

另外自由函數使用參數相關查找。這意味着該功能應該放在命名空間中的類是在我愛:

#include <vector> 
namespace std { 
    int hash(vector<int> const &vec) { /*...*/ } 
} 
//... 
std::vector<int> v; 
//... 
hash(v); 

現在你可以還叫hash不合格的,但看不到它用於任何其他目的,除非你做using namespace std(我個人幾乎從來沒有這樣做,只是使用前綴std::或做using std::vector來得到我想要的符號)。不幸的是,我不確定在另一個命名空間中,與名稱空間相關的查找如何與typedef一起使用。

在很多模板算法中,自由功能—和相當通用的名稱—通常用於替代方法,因爲它們可以添加到現有類中,可以爲基元類型定義或兩者都定義。

+2

你不能在std命名空間中添加任何新東西。 – 2011-04-12 06:47:47

+1

@丹尼斯:是的,你是。這不是你可以添加的東西之一。您允許添加的主要內容是現有模板的新特化,專用於用戶定義的類型。 – 2011-04-12 13:24:06