2013-01-15 58 views
0

我有一種情況,我有一個類的層次結構:WidgetDoobry是從Base繼承的對象類型(實際上有2個以上的類型)。對象的每個實例都有一個屬性列表。有一些屬性對於所有對象都是通用的,還有一些屬性是針對每個項目類型的。一個簡單的實現可能是這樣的:爲屬性表創建調試友好的枚舉

enum PropertyType { 
    COMMON_SIZE=0,    // first section: properties common to all 
    COMMON_POSITION, 
    ... 
    WIDGET_PROPERTY_START=100, // ensure enough space for expansion 
    WIDGET_DONGLE_SIZE, 
    WIDGET_TEXT, 
    ... 
    DOOBRY_PROPERTY_START=200 
    DOOBRY_COLOUR 
    .... 
}; 

class Base { 
public: 
    Base(); 

    std::vector<std::pair<PropertyType, string>> properties; 
}; 

這實現了一個目標在調試,我可以看到映射到有意義的名稱屬性的列表。但是它有一些缺點:對所有項目

  • 屬性都在一個標題中定義(用於封裝不好)
  • 我們還要挑選一些任意號碼,每個班級開始位置,以便有足夠的空間爲將來的擴展,如果我們添加更多的屬性給其中一個類。

我的問題是,是否有另一種方式來實現這一目標。一個想法是,我可以使用字符串常量,這些字符串常量的存儲空間較大,查找速度較慢,但​​具有使名稱唯一且每個項目類型可以定義其自己的屬性更容易的優點。

編輯: 這是需要的屬性將被序列化,所以必須是穩定的隨着時間的推移(即枚舉不會改變)。可能有多達1M個對象,但絕大多數對象將具有空的屬性表(因爲它們使用默認值)。查詢性能比插入更重要,做字符串哈希的性能命中可能可以忽略不計(我們無法衡量它是否尚未寫入!)。

+0

你在序列化這些嗎?也就是說,值是否必須隨着時間的推移而保持穩定?記憶足跡有多重要?性能 - 「PropertyType」上的哪些操作需要快速執行?你的調試器是什麼 - 你能編寫腳本來解釋它的值嗎? – Yakk

+0

序列化:是的,這些必須是穩定的。內存佔用:對於絕大多數項目來說屬性表都是空的,這不是太重要。性能:查找速度應該很快,但考慮到大多數表將是空的,字符串散列的計算可以忽略不計。調試器:VS2010和Xcode 4 - 我們廣泛使用調試器可視化器 –

回答

1
struct UniqueTag { 
    friend TagManager; 
    char const* tag; 
    UniqueTag(UniqueTag const& other):tag(other.tag) {} 
    UniqueTag():tag(nullptr) {}; // being able to create the null tag is useful 
    bool operator<(UniqueTag const& other)const{ return tag < other.tag; } 
    bool operator==(UniqueTag const& other)const{ return tag == other.tag; } 
    // do other operators 
private: 
    UniqueTag(char const* t):tag(t) {} 
}; 

#include <map> // or unordered_map for performance 
class TagManager { 
    std::map<std::string, UniqueTag> cache; 
    std::vector< std::unique_ptr<char[]> > data; 
public: 
    TagManager() {}; 
    UniqueTag GetTag(std::string s) { 
    auto range = cache.equal_range(s); 
    if (range.first != range.second) { 
     return range.first->second; 
    } 
    std::unique_ptr<char[]> str(new char[ s.size()+1 ]); 
    std::copy(s.begin(), s.end(), &str[0]); 
    str[s.length()] = '\0'; 
    UniqueTag retval(str.get()); 
    data.push_back(std::move(str)); 
    if(s.length()==0) { 
     retval = UniqueTag(); // empty string is null tag, so we don't have both! 
    } 
    cache.insert(range.first, make_pair(std::move(s), retval)); 
    return retval; 
    } 
}; 

TagManager保持到串一串獨特的指針。我們可以進行快速比較,因爲我們比較指針值。從一個字符串轉換爲其中一個獨特的標籤是緩慢的,並且它具有隱含的單個標籤管理器的反模式,但是...

替代版本包括讓你的UniqueTag在它旁邊粘貼一個散列,然後看(在某種程度上斷言在調試中沒有兩個字符串哈希到相同的值 - 生日悖論使得發生這種情況的可能性比天真預期的要大得多)。這樣就擺脫了單個管理器類(至少在發行版中 - 在調試中,你將有一種檢測衝突的方法。如果你的哈希是確定性的,調試中沒有碰撞可能意味着在發佈時沒有衝突)。

A boost::variant<enum1, enum2, enum3>與適當的可視化和一些運營商超載會讓你有多個獨立的enum s。或者在enum es的家庭式釀酒工會,主要的enum表示哪一個是有效的,在其上面有一個可視化工具可以讓您將管理權限分散到各地。在這兩種情況下,您都會輸出enum「類型」的索引,然後輸出enum的值 - 因此enum的順序必須穩定,並且每個enum內的值必須穩定,但不得有魔術整數需要。爲了檢查是否相等,需要兩個整數鏈接的比較,而不是一個(如果速度更快,可以使用一個64位比較)。

+0

關於使用唯一char *指針的有趣想法 - 可以工作。 「變體」實現的一個潛在問題是它將單獨的枚舉關聯到一個依賴於各個類型的單一結構中。也可能有很多類,超過'boost :: variant'可以應付。 –

+0

我越想這件事,我越喜歡string - > char * map的想法。整齊! –