C++ std::any的模擬實(shí)現(xiàn)
std::any
std::any是C++標(biāo)準(zhǔn)庫中的一個(gè)類,官網(wǎng)對(duì)它的描述如下:
類 any 描述用于任何可拷貝構(gòu)造類型的單個(gè)值的類型安全容器。
類 any 的對(duì)象存儲(chǔ)任何滿足構(gòu)造函數(shù)要求的類型的一個(gè)實(shí)例或?yàn)榭?,而這被稱為 any 類對(duì)象的狀態(tài)。存儲(chǔ)的實(shí)例被稱作所含對(duì)象。若兩個(gè)狀態(tài)均為空,或均為非空且其所含對(duì)象等價(jià),則兩個(gè)狀態(tài)等價(jià)。
非成員 any_cast 函數(shù)提供對(duì)所含對(duì)象的類型安全訪問。
換句話說,std::any對(duì)象可以存儲(chǔ)任何類型的數(shù)據(jù)(單例等特殊情況除外)。這篇文章來探討一下如何自己實(shí)現(xiàn)一個(gè)Any類。
Any的基本原理
在C++這種強(qiáng)類型語言中,想用一種類型來保存多種類型的數(shù)據(jù),首先想到的就是用父類指針(或引用)來保存子類,實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)。但問題是,我們想要保存任意類型,必須使所有類型都有一個(gè)公共的父類。在某些語言(如Java)中,有一個(gè)Object類,是所有類的父類,因此這種語言中就非常容易實(shí)現(xiàn)。但C++的類型系統(tǒng)相當(dāng)混亂,原生類型沒有父類,STL的類型也沒有一個(gè)公共父類,而自定義類型也不會(huì)自動(dòng)繼承自一個(gè)公共父類,因此直接用父類指針不可行。但是如果我們把模板和繼承結(jié)合一下就可以了,為每一種類型創(chuàng)建一個(gè)對(duì)應(yīng)的模板類,這個(gè)模板類又繼承自一個(gè)父類。核心代碼如下:
class AnyHelperBase { }; template<typename T> class AnyHelper :public AnyHelperBase { T data; };
這樣我們就可以用AnyHelperBase*
類型來存儲(chǔ)任意類型的數(shù)據(jù)了。當(dāng)然,這只是大體思路,還需要具體完善。下面我們將以上述代碼為母體,添加功能。
將數(shù)據(jù)存儲(chǔ)到Any
Any類
在上面的代碼中,如何將數(shù)據(jù)存儲(chǔ)到Any?肯定需要一個(gè)AnyHelperBase*
的類型。但考慮到直接操作指針不是很方便,并且std::any使用的時(shí)候并不需要指針,我們應(yīng)該再寫一個(gè)類來維護(hù)AnyHelperBase*
。
class Any { private: class AnyHelperBase { public: }; template<typename T> class AnyHelper :public AnyHelperBase { public: T data; }; AnyHelperBase* data; public: };
構(gòu)造函數(shù)
接下來實(shí)現(xiàn)AnyHelper的構(gòu)造函數(shù)(第一個(gè)是就地構(gòu)造,直接通過參數(shù)構(gòu)造data,后兩個(gè)是拷貝構(gòu)造):
template<typename ...Args> AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {} AnyHelper(const AnyHelper& other) :data(other.data) {} AnyHelper(const T& value) :data(value) {}
Any類的構(gòu)造函數(shù):
Any() :data(nullptr) {} template<typename T> Any(const T& value) : data(new AnyHelper<std::decay_t<T>>(value)) {} //Any(const Any& other) :data( ??? ) {} Any(Any&& other) :data(other.data) { other.data = nullptr; }
注意:std::decay_t<T>
的作用是去掉T的const,引用等亂七八糟的屬性,比如std::decay_t<const int&>
的結(jié)果是int
。例如,我們顯然不希望傳入const int
與int
得到不同的結(jié)果。這一點(diǎn)很重要,因?yàn)?strong>如果類型不匹配,后面獲取數(shù)據(jù)時(shí)就會(huì)拋出異常!
拷貝構(gòu)造的困難和解決方案
在寫拷貝構(gòu)造(上面代碼的第三個(gè)函數(shù))時(shí),我們遇到了問題。由于是深拷貝,我們肯定不能直接復(fù)制指針,而是應(yīng)該再new一個(gè)對(duì)象。但問題是,我們?cè)趺传@取另一個(gè)Any中的類型呢?這個(gè)問題似乎不好解決,因?yàn)?strong>只有在AnyHelper類內(nèi)部我們才會(huì)知道存儲(chǔ)的類型(這句話很重要)。但我們可以變通一下,讓AnyHelper類直接返回一個(gè)自身的拷貝的指針,我們不必關(guān)心他具體是什么類型。當(dāng)然,我們使用的是AnyHelperBase*
,所以AnyHelperBase類里必須就得有這個(gè)函數(shù),換句話說,這得是一個(gè)虛函數(shù)。在這里我們又用到了多態(tài)的特性。往AnyHelperBase和AnyHelper中添加Clone函數(shù):
class AnyHelperBase { public: virtual AnyHelperBase* Clone()const = 0; }; template<typename T> class AnyHelper :public AnyHelperBase { public: T data; template<typename ...Args> AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {} AnyHelper(const AnyHelper& other) :data(other.data) {} AnyHelper(const T& value) :data(value) {} virtual AnyHelper* Clone()const { return new AnyHelper(*this); } };
Any類的拷貝構(gòu)造函數(shù):
Any(const Any& other) :data(other.data->Clone()) {}
賦值運(yùn)算符
賦值運(yùn)算符和構(gòu)造函數(shù)基本一樣,需要注意的是delete原來的data。
template<typename T> Any& operator=(const T& value) { if (data != nullptr) delete data; data = new AnyHelper<std::decay_t<T>>(value); return *this; } Any& operator=(const Any& other) { if (data != nullptr) delete data; data = other.data->Clone(); return *this; } Any& operator=(Any&& other) { if (data != nullptr) delete data; data = other.data; other.data = nullptr; return *this; }
其他賦值類函數(shù)
注意到std::any可以有空值,并且可以設(shè)置為空,我們也寫一個(gè)Reset函數(shù)將Any設(shè)為空。
void Reset() { if (data != nullptr) delete data; data = nullptr; }
另外,為了優(yōu)化性能,并且支持一些不可移動(dòng)和拷貝的類型,我們添加就地構(gòu)造函數(shù),可以直接通過參數(shù)構(gòu)造一個(gè)對(duì)象。
template<typename T, typename ...Args> std::decay_t<T>& Emplace(Args&&... args) { if (data != nullptr) delete data; auto temp = new AnyHelper<std::decay_t<T>>(std::forward<Args>(args)...); data = temp; return temp->data; }
還有一個(gè)簡(jiǎn)單的Swap,直接交換data指針:
void Swap(Any& other) { AnyHelperBase* temp = this->data; this->data = other.data; other.data = temp; }
到這里,Any類就可以存儲(chǔ)數(shù)據(jù)了。
從Any獲取數(shù)據(jù):Any轉(zhuǎn)換為其他類型
對(duì)一個(gè)實(shí)用的Any類來說,獲取數(shù)據(jù)也是必不可少的,實(shí)現(xiàn)獲取數(shù)據(jù)即將Any轉(zhuǎn)換為其他類型。對(duì)std::any來說,有std::any_cast函數(shù)來實(shí)現(xiàn)這一轉(zhuǎn)換,我們也寫一個(gè)AnyCast函數(shù)。
template<typename T> T AnyCast(const Any& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> T AnyCast(Any& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> T AnyCast(Any&& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> const T* AnyCast(const Any* any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any->data); if (p == nullptr) return nullptr; return &p->data; } template<typename T> T* AnyCast(Any* any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any->data); if (p == nullptr) return nullptr; return &p->data; }
AnyCast一共有5個(gè)重載(和STL中的一致),前三個(gè)是一組,后兩個(gè)是一組。前三個(gè)的特性是轉(zhuǎn)換失敗會(huì)拋出異常,后兩個(gè)接受指針,返回指針,失敗不會(huì)拋異常,而是會(huì)返回空指針。5個(gè)函數(shù)實(shí)現(xiàn)原理都是一樣的,核心就是使用dynamic_cast將AnyHelperBase*
類型的data轉(zhuǎn)換為相應(yīng)的AnyHelper子類。dynamic_cast是向下轉(zhuǎn)換的操作符,也支持運(yùn)行時(shí)多態(tài),如果轉(zhuǎn)換失敗會(huì)返回空指針。
獲取Any信息
到現(xiàn)在,一個(gè)Any類的核心功能已經(jīng)全部完成,不過為了模擬std::any,我們還是再添加一些獲取信息的函數(shù)。
獲取類型的type_info
我們前面說過,在我們實(shí)現(xiàn)的Any類中,只有在AnyHelper類內(nèi)部我們才會(huì)知道存儲(chǔ)的類型。因此,獲取類型必須從AnyHelper類下首。類似于Clone,我們?cè)贋锳nyHelperBase和AnyHelper添加一個(gè)虛函數(shù):
class AnyHelperBase { public: virtual const std::type_info& Type()const = 0; virtual AnyHelperBase* Clone()const = 0; }; template<typename T> class AnyHelper :public AnyHelperBase { public: T data; //構(gòu)造函數(shù)省略 //... virtual const std::type_info& Type()const { return typeid(T); } virtual AnyHelper* Clone()const { return new AnyHelper(*this); } };
這樣Any類的Type就好寫了:
const std::type_info& Type()const { return data->Type(); }
HasValue
沒啥好說的…
bool HasValue()const { return data != nullptr; }
析構(gòu)函數(shù)
在這里我提醒一下大家,雖然析構(gòu)函數(shù)很簡(jiǎn)單,但一定不要忘了寫,否則會(huì)引起內(nèi)存泄漏!檢查析構(gòu)函數(shù)是一個(gè)很好的代碼習(xí)慣!
~Any() { if (data != nullptr) delete data; }
附錄:完整代碼
到這里,整個(gè)Any類就完成了。下面是完整代碼:
namespace MyStd { class Any { private: class AnyHelperBase { public: virtual const std::type_info& Type()const = 0; virtual AnyHelperBase* Clone()const = 0; }; template<typename T> class AnyHelper :public AnyHelperBase { public: T data; template<typename ...Args> AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {} AnyHelper(const AnyHelper& other) :data(other.data) {} AnyHelper(const T& value) :data(value) {} virtual const std::type_info& Type()const { return typeid(T); } virtual AnyHelper* Clone()const { return new AnyHelper(*this); } }; template<typename T> friend T AnyCast(const Any& any); template<typename T> friend T AnyCast(Any& any); template<typename T> friend T AnyCast(Any&& any); template<typename T> friend const T* AnyCast(const Any* any); template<typename T> friend T* AnyCast(Any* any); AnyHelperBase* data; public: Any() :data(nullptr) {} template<typename T> Any(const T& value) : data(new AnyHelper<std::decay_t<T>>(value)) {} Any(const Any& other) :data(other.data->Clone()) {} Any(Any&& other) :data(other.data) { other.data = nullptr; } const std::type_info& Type()const { return data->Type(); } bool HasValue()const { return data != nullptr; } void Reset() { if (data != nullptr) delete data; data = nullptr; } template<typename T> Any& operator=(const T& value) { if (data != nullptr) delete data; data = new AnyHelper<std::decay_t<T>>(value); return *this; } Any& operator=(const Any& other) { if (data != nullptr) delete data; data = other.data->Clone(); return *this; } Any& operator=(Any&& other) { if (data != nullptr) delete data; data = other.data; other.data = nullptr; return *this; } void Swap(Any& other) { AnyHelperBase* temp = this->data; this->data = other.data; other.data = temp; } template<typename T, typename ...Args> std::decay_t<T>& Emplace(Args&&... args) { if (data != nullptr) delete data; auto temp = new AnyHelper<std::decay_t<T>>(std::forward<Args>(args)...); data = temp; return temp->data; } ~Any() { if (data != nullptr) delete data; } }; template<typename T> T AnyCast(const Any& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> T AnyCast(Any& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> T AnyCast(Any&& any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any.data); if (p == nullptr) throw std::runtime_error("Bad any cast!"); return p->data; } template<typename T> const T* AnyCast(const Any* any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any->data); if (p == nullptr) return nullptr; return &p->data; } template<typename T> T* AnyCast(Any* any) { auto p = dynamic_cast<Any::AnyHelper<std::decay_t<T>>*>(any->data); if (p == nullptr) return nullptr; return &p->data; } }
到此這篇關(guān)于C++ std::any的模擬實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ std::any內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++中std::sort函數(shù)介紹和使用場(chǎng)景
- C++中std::find函數(shù)介紹和使用場(chǎng)景
- C++中std::count函數(shù)介紹和使用場(chǎng)景
- C++17中std::string_view的使用
- 深入理解C++中std::chrono庫的使用
- C++學(xué)習(xí)筆記std::vector底層原理及擴(kuò)容
- C++筆記之std::future的用法小結(jié)
- C++ std::condition_variable 條件變量用法解析
- C++ std::unique_lock 用法實(shí)例詳解
- C++ std::map幾種遍歷方式(正序倒序)
相關(guān)文章
重構(gòu)-C++實(shí)現(xiàn)矩陣的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄貥?gòu)-C++實(shí)現(xiàn)矩陣的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實(shí)例
這篇文章主要介紹了C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07