2013-10-24 30 views
3

我正在研究一個C++項目(Embarcadero C++ Builder),其中許多類(都在同一個命名空間中)需要從顏色名稱中獲取顏色值。爲了實現這一點,我創建的函數getColorCode()由所有類所使用的,在單獨的一段代碼,Tools.cpp,如下圖所示:函數的單個實例

#include "Tools.h" 
namespace mynamespace { 
int getColorCode(std::string Name) 
{ 
    if (Name == "red") return(0xFF0000); 
    else if (Name == "green") return(0x00FF00); 
    else if (Name == "blue") return(0x0000FF); 
    // many more else ifs 
    else return(0x000000); 
} 
} 

頭文件是:

#ifndef MYNAMESPACE_TOOLS_H 
#define MYNAMESPACE_TOOLS_H 
#include <string> 
namespace mynamespace { 
    int getColorCode(std::string Name); 
} 
#endif 

這作品,但我希望將所有顏色定義存儲在地圖中以避免數百個其他ifs。我的問題是,我無法在Tools.cpp的頭文件中定義類似std::map<std::string, int> ColorNames;的東西,也沒有在定義地圖的線上獲得W8058 Cannot create pre-compiled header。另外,我得到了幾個鏈接器警告,mynamespace :: ColorNames在每個類中定義,包括Tools.h。

我的計劃是在第一次調用getColorCode()時通過檢查map.empty()來填充地圖,並將所有的顏色名稱和代碼添加到它,如果它是空的,所以更多的調用將只搜索地圖。

另一個嘗試是爲此創建工具類並在構造函數中初始化映射。但是然後每個使用它的課程創建一個它自己的實例,我不想要。閱讀關於單身人士的討論並嘗試提出的代碼並沒有幫助。

是否有任何實際的方法來實現這個或我應該留在醜陋的(非表演)if-then-else鏈?

感謝您的任何提示,阿明

+1

你爲什麼預設if-then-else鏈是非高性能的?你有分析過嗎?我相信你通過比較*字符串*來獲得更大的命中,那麼你會從一個if-then-else鏈中獲得。你在這裏咆哮錯誤的樹。 –

+1

您需要一個命名空間範圍的變量,例如'std :: map ColprNames;''namespace mynamespace'內'Tools.cpp'中的某處。保留'getColorCode()'函數,但使用地圖。 –

+0

@ n.m。 : 謝謝你的評論。我嘗試了它與下面的重新運行類似的提示,它解決了我的問題。 – Armin

回答

0

文件級靜態接縫這裏是合適的我但您需要在初始化時處理競爭條件。不過,我可能會建議只爲由int取代的顏色名稱創建宏。或者說,擁有所有常量承包商,客人

文件級靜態

static std::map<string,int> Colors; 
static std::mutex lck; 
int getColorCode(std::string Name) 
{ 
    // Get a lock here if you are not single threaded 
    lck.lock() 
    if(Colors.empty()) 
    {...} 
    lck.unlock() 
} 

或具有所有顏色

class Colors 
{ 
    Colors(): 
     red(0xFF0000) 
    { 

    } 
    int red; 
} 
+0

不應該是'靜態std :: mutex lck;'?但不錯的想法,但它使線程安全! –

+0

好的。 – rerun

+0

函數體內的'static'聲明就足夠了。對於你的類的想法(這也很好),你應該詳細說明一下用法,而'int red'應該是'public:const int red'恕我直言,或者甚至簡單地說'static const'。但是,如果主鍵是一個字符串,主鍵是一個字符串... –

-1

只要把地圖在你的源文件,並使用該函數來檢索值:

#include "Tools.h" 
namespace mynamespace { 
    std::map<std::string, int> ColorNames; 
    int getColorCode(std::string Name) 
    { 
     // do some error checking 
     // assume ColorNames has already been populated somehow 
     return ColorNames[Name]; 
    } 
} 
+0

恕我直言OP的問題主要不是指如何從地圖訪問條目,而是如何方便地使用正確的值填充它! –

+0

他正嘗試使用單個訪問點來檢索顏色值,問題標題表明他希望爲此創建某種單例模式。我不認爲它是'我如何初始化我的地圖'類型的問題 – Ben

+0

'ColorNames'應該放在模塊本地,但... –

1

如果你可以使用C++ 11一類的類(我不熟悉C++ builder),你可以在函數中初始化靜態映射,如

int getColorCode(std::string name) { 
    static std::map<std::string, int> colors{ 
     { "red", 0xFF0000 }, 
     { "green", 0x00FF00 }, 
     // ... etc 
    }; 

    // rest of logic. 
} 

這樣做的好處是t他的地圖被本地化爲函數,初始化一次,只有一次,並且不能從外部世界訪問。

如果您沒有C++ 11功能(再次,我不知道編譯器),只需檢查地圖是否像您說的那樣是空的,然後填充它。我仍然將它標記爲靜態,但全局變量很糟糕。

0

我會使用簡單的struct聲明來代替地圖。只要你沒有上百萬的參賽作品,這應該不會讓你在使用std::map時有明顯的表現。檢查爲O(n) VS O(log n)的複雜性,如果它符合您的需求爲ñ項,但我認爲n的數百的範圍(如你所提到的)不應該太。

只是你的.cpp裏面做:

namespace { 
    struct ColorDef 
    { 
     std::string name; 
     int colorValue; 
    }; 

    ColorDef colorValueDefs[] = { 
     // Put your entries here: 
     { "red", 0xFF0000 } , 
     { "green", 0x00FF00 } , 
     { "blue", 0x0000FF } , 
     // etc. ... 
     // { "" , -1 } // have an end marker if you don't want to use sizeof() 
         // to iterate through 
    }; 
} 

namespace mynamespace { 
    int getColorCode(std::string Name) { 
     // Make it thread safe if necessary: 
     // static std::mutex mtx; 
     // std::lock(mtx); 
     for(int i = 0; i < sizeof(colorValueDefs); ++i) { 
      if(Name == colorValueDefs[i].name) { 
       return colorValueDefs[i].colorValue; 
      } 
     } 
     return -1; 
    } 
} 

如果你可以使用標準語法,你可以初始化std::map的方式如上圖所示colorValueDefs陣列相同。

UPDATE
OK,我想我在這裏講廢話緊靠查找性能!如果我沒有得到圖表,性能命中開始在非常低的數字出現的ñ,檢查圖:n = 10
我不知道要得到正確的表示形式爲O(n)/O(日誌n)這裏雖然: -/...
std::map應該是更好的選擇,然後性能明智!在第一次調用函數時初始化時,您會得到一個小小的打擊,或者在使用初始化程序列表功能初始化映射時可以避免這種情況。