C++ std::any的模擬實(shí)現(xiàn)
std::any
std::any是C++標(biāo)準(zhǔn)庫中的一個(gè)類,官網(wǎng)對它的描述如下:
類 any 描述用于任何可拷貝構(gòu)造類型的單個(gè)值的類型安全容器。
類 any 的對象存儲(chǔ)任何滿足構(gòu)造函數(shù)要求的類型的一個(gè)實(shí)例或?yàn)榭?,而這被稱為 any 類對象的狀態(tài)。存儲(chǔ)的實(shí)例被稱作所含對象。若兩個(gè)狀態(tài)均為空,或均為非空且其所含對象等價(jià),則兩個(gè)狀態(tài)等價(jià)。
非成員 any_cast 函數(shù)提供對所含對象的類型安全訪問。
換句話說,std::any對象可以存儲(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è)對應(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è)對象。但問題是,我們怎么獲取另一個(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è)對象。
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è)簡單的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)換為其他類型
對一個(gè)實(shí)用的Any類來說,獲取數(shù)據(jù)也是必不可少的,實(shí)現(xiàn)獲取數(shù)據(jù)即將Any轉(zhuǎn)換為其他類型。對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,我們再為AnyHelperBase和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ù)很簡單,但一定不要忘了寫,否則會(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
重構(gòu)-C++實(shí)現(xiàn)矩陣的簡單實(shí)例
下面小編就為大家?guī)硪黄貥?gòu)-C++實(shí)現(xiàn)矩陣的簡單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實(shí)例
這篇文章主要介紹了C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-07-07

