1. 程式人生 > 其它 >重定義 不同的基型別_c++ 型別擦除

重定義 不同的基型別_c++ 型別擦除

技術標籤:重定義 不同的基型別

22d7cc4848f1dd5cdbca0cf3c942c5ef.png

什麼是型別擦除?

型別擦除就是將原有型別消除或者隱藏。

為什麼要型別擦除?

因為很多時候並不需要關心具體型別是什麼或者根本就不需要這個型別,通過型別擦除可以獲取很多好處,例如更好的擴充套件性、消除耦合以及一些重複行為,使程式更加簡潔高效。

c++中型別擦除方式主要有以下五種:

  • 多型
  • 模板
  • 容器
  • 通用型別
  • 閉包

通過多型擦除型別

該方式最簡單也是經常用的,將派生型別隱式轉換成基型別,再通過基類去多型的呼叫行為。

在這種情況下,不用關心派生類的具體型別,只需要以統一的方式去做不同的事情,所以就把派生型別轉成基型別隱藏起來,這樣不僅僅可以多型呼叫還使程式具有良好的可擴充套件性。

然而這種方式的型別擦除僅僅是部分的型別擦除,因為基型別仍然存在,而且這種型別擦除的方式必須是繼承的方式,繼承使得兩個物件強烈的耦合在一起。所以通過多型來擦除型別的方式有較多侷限性。

通過模板擦除型別

該方式本質上是對不同型別的共同行為進行抽象,不同型別彼此之間不需要通過繼承這種強耦合的方式去獲得共同的行為。僅通過模板就能獲取共同行為,降低不同型別之間的耦合,是一種很好的型別擦除方式。

然而,雖然降低了物件間的耦合,但是基本型別始終需要指定,並沒有消除基本型別。

通過容器擦除型別

boost.variant 可以把各種型別包起來,從而獲得一種統一的型別,而且不同型別的物件間沒有耦合關係,它僅僅是一個型別的容器。示例:

struct blob
{
    const char *pBuf;
    int size;
};

//定義通用的型別,這個型別可能容納多種型別
typedef boost::variant<double, int, uint32_t, sqlite3_int64, char*, blob, NullType>Value;

vector<Value> vt; //通用型別的容器,這個容器現在就可以容納上面的那些型別的物件了
vt.push_back(1);
vt.push_back("test");
vt.push_back(1.22);
vt.push_back({"test", 4}); 

示例中擦除了不同型別,使得不同的型別都可以放到一個容器中。取值的時候,通過get<T>(Value)就可以獲取對應型別的值。

這種方式是通過某種容器把型別包起來,從而達到型別擦除的目的。缺點是這個通用的型別必須事先定義好,只能容納宣告的型別,增加一種新型別就不行了。

通過通用型別擦除型別

該方式可以消除容器擦除型別的缺點,類似於c#和java中的object型別。這種通用型別是通過boost.any實現的,它不需要預先定義型別,不同型別都可以轉成any。示例:

unordered_map<string, boost::any> m_creatorMap;
m_creatorMap.insert(make_pair(strKey, new T)); //T may be any type

boost::any obj = m_creatorMap[strKey];
T t = boost::any_cast<T>(obj);

但是還存一個缺點,就是取值的時候仍然依賴於具體型別,無論是通過get<T>還是any_case<T>,都需要T的具體型別,這在某種情況下仍然有侷限性。

通過閉包擦除型別

閉包可以稱為匿名函式或lamda表示式,c++11中的lamda表示式就是c++中的閉包。

c++11引入lamda,實際上引入了函數語言程式設計的概念,函數語言程式設計有很多優點,使程式碼更簡潔,而且宣告式的編碼方式更貼近人的思維方式。函數語言程式設計在更高的層次上對不同型別的公共行為進行了抽象,從而使我們不必去關心具體型別。示例:

std::map<int, std::function <void()>> m_freeMap; //儲存返回出去的記憶體塊

template<typename R, typename T>
R GetResult()
{
    R result = GetTable<R, T>();    

    m_freeMap.insert(std::make_pair(result.sequenceId, [this, result]
    {
        FreeResult(result);        
    }));
}

bool FreeResultById(int& memId)
{
    auto it = m_freeMap.find(memId);
    if (it == m_freeMap.end())
        return false;

    it->second();       //delete by lamda
    m_freeMap.erase(memId);

    return true;
}   

總結

通過閉包去擦除型別,可以解決前面四種擦除方式遇到的問題,優雅而簡單!