最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ std::any的模擬實現(xiàn)

 更新時間:2024年02月03日 15:00:55   作者:[PE]經(jīng)典八炮  
std::any是C++標準庫中的一個類,std::any對象可以存儲除單例等特殊情況外的任何類型的數(shù)據(jù),本文主要介紹了C++ std::any的模擬實現(xiàn),具有一定的參考價值,感興趣的可以了解一下

std::any

std::any是C++標準庫中的一個類,官網(wǎng)對它的描述如下:

類 any 描述用于任何可拷貝構造類型的單個值的類型安全容器。

類 any 的對象存儲任何滿足構造函數(shù)要求的類型的一個實例或為空,而這被稱為 any 類對象的狀態(tài)。存儲的實例被稱作所含對象。若兩個狀態(tài)均為空,或均為非空且其所含對象等價,則兩個狀態(tài)等價。

非成員 any_cast 函數(shù)提供對所含對象的類型安全訪問。

換句話說,std::any對象可以存儲任何類型的數(shù)據(jù)(單例等特殊情況除外)。這篇文章來探討一下如何自己實現(xiàn)一個Any類。

Any的基本原理

在C++這種強類型語言中,想用一種類型來保存多種類型的數(shù)據(jù),首先想到的就是用父類指針(或引用)來保存子類,實現(xiàn)運行時多態(tài)。但問題是,我們想要保存任意類型,必須使所有類型都有一個公共的父類。在某些語言(如Java)中,有一個Object類,是所有類的父類,因此這種語言中就非常容易實現(xiàn)。但C++的類型系統(tǒng)相當混亂,原生類型沒有父類,STL的類型也沒有一個公共父類,而自定義類型也不會自動繼承自一個公共父類,因此直接用父類指針不可行。但是如果我們把模板和繼承結合一下就可以了,為每一種類型創(chuàng)建一個對應的模板類,這個模板類又繼承自一個父類。核心代碼如下:

class AnyHelperBase
{
};
template<typename T>
class AnyHelper :public AnyHelperBase
{
	T data;
};

這樣我們就可以用AnyHelperBase*類型來存儲任意類型的數(shù)據(jù)了。當然,這只是大體思路,還需要具體完善。下面我們將以上述代碼為母體,添加功能。

將數(shù)據(jù)存儲到Any

Any類

在上面的代碼中,如何將數(shù)據(jù)存儲到Any?肯定需要一個AnyHelperBase*的類型。但考慮到直接操作指針不是很方便,并且std::any使用的時候并不需要指針,我們應該再寫一個類來維護AnyHelperBase*。

class Any
{
private:
	class AnyHelperBase
	{
	public:
	};
	template<typename T>
	class AnyHelper :public AnyHelperBase
	{
	public:
		T data;
	};
	AnyHelperBase* data;
public:
	
};

構造函數(shù)

接下來實現(xiàn)AnyHelper的構造函數(shù)(第一個是就地構造,直接通過參數(shù)構造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) {}

Any類的構造函數(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&>的結果是int。例如,我們顯然不希望傳入const intint得到不同的結果。這一點很重要,因為如果類型不匹配,后面獲取數(shù)據(jù)時就會拋出異常!

拷貝構造的困難和解決方案

在寫拷貝構造(上面代碼的第三個函數(shù))時,我們遇到了問題。由于是深拷貝,我們肯定不能直接復制指針,而是應該再new一個對象。但問題是,我們怎么獲取另一個Any中的類型呢?這個問題似乎不好解決,因為只有在AnyHelper類內(nèi)部我們才會知道存儲的類型(這句話很重要)。但我們可以變通一下,讓AnyHelper類直接返回一個自身的拷貝的指針,我們不必關心他具體是什么類型。當然,我們使用的是AnyHelperBase*,所以AnyHelperBase類里必須就得有這個函數(shù),換句話說,這得是一個虛函數(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類的拷貝構造函數(shù):

Any(const Any& other) :data(other.data->Clone()) {}

賦值運算符

賦值運算符和構造函數(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可以有空值,并且可以設置為空,我們也寫一個Reset函數(shù)將Any設為空。

void Reset()
{
	if (data != nullptr)
		delete data;
	data = nullptr;
}

另外,為了優(yōu)化性能,并且支持一些不可移動和拷貝的類型,我們添加就地構造函數(shù),可以直接通過參數(shù)構造一個對象。

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;
}

還有一個簡單的Swap,直接交換data指針:

void Swap(Any& other)
{
	AnyHelperBase* temp = this->data;
	this->data = other.data;
	other.data = temp;
}

到這里,Any類就可以存儲數(shù)據(jù)了。

從Any獲取數(shù)據(jù):Any轉換為其他類型

對一個實用的Any類來說,獲取數(shù)據(jù)也是必不可少的,實現(xiàn)獲取數(shù)據(jù)即將Any轉換為其他類型。對std::any來說,有std::any_cast函數(shù)來實現(xiàn)這一轉換,我們也寫一個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個重載(和STL中的一致),前三個是一組,后兩個是一組。前三個的特性是轉換失敗會拋出異常,后兩個接受指針,返回指針,失敗不會拋異常,而是會返回空指針。5個函數(shù)實現(xiàn)原理都是一樣的,核心就是使用dynamic_cast將AnyHelperBase*類型的data轉換為相應的AnyHelper子類。dynamic_cast是向下轉換的操作符,也支持運行時多態(tài),如果轉換失敗會返回空指針。

獲取Any信息

到現(xiàn)在,一個Any類的核心功能已經(jīng)全部完成,不過為了模擬std::any,我們還是再添加一些獲取信息的函數(shù)。

獲取類型的type_info

我們前面說過,在我們實現(xiàn)的Any類中,只有在AnyHelper類內(nèi)部我們才會知道存儲的類型。因此,獲取類型必須從AnyHelper類下首。類似于Clone,我們再為AnyHelperBase和AnyHelper添加一個虛函數(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;
	//構造函數(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;
}

析構函數(shù)

在這里我提醒一下大家,雖然析構函數(shù)很簡單,但一定不要忘了寫,否則會引起內(nèi)存泄漏!檢查析構函數(shù)是一個很好的代碼習慣!

~Any()
{
	if (data != nullptr)
		delete data;
}

附錄:完整代碼

到這里,整個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;
	}
}

到此這篇關于C++ std::any的模擬實現(xiàn)的文章就介紹到這了,更多相關C++ std::any內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • C語言超全面講解函數(shù)的使用方法下

    C語言超全面講解函數(shù)的使用方法下

    函數(shù)是一組一起執(zhí)行一個任務的語句。每個?C?程序都至少有一個函數(shù),即主函數(shù)?main()?,所有簡單的程序都可以定義其他額外的函數(shù),由于篇幅過大,分為兩篇講解,下面開始下篇
    2022-04-04
  • C語言實現(xiàn)可排序通訊錄的示例代碼

    C語言實現(xiàn)可排序通訊錄的示例代碼

    本文主要介紹了C語言實現(xiàn)可排序通訊錄的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • VC++實現(xiàn)程序開機啟動運行的方法

    VC++實現(xiàn)程序開機啟動運行的方法

    這篇文章主要介紹了VC++實現(xiàn)程序開機啟動運行的方法,很實用的功能,需要的朋友可以參考下
    2014-08-08
  • C++函數(shù)指針+對象指針+this指針+指向類靜態(tài)和非靜態(tài)成員的指針

    C++函數(shù)指針+對象指針+this指針+指向類靜態(tài)和非靜態(tài)成員的指針

    這篇文章主要介紹了C++函數(shù)指針+對象指針+this指針+指向類靜態(tài)和非靜態(tài)成員的指針,函數(shù)指針定義和賦值的語法指其中數(shù)據(jù)類型代表指向函數(shù)的返回類型,形參表為指向函數(shù)的形參表,更多相關資料需要的朋友可以參考一下下面文章內(nèi)容
    2022-03-03
  • C++連連看判定圖形消除算法

    C++連連看判定圖形消除算法

    這篇文章主要為大家詳細介紹了C++連連看判定圖形消除算法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • 數(shù)據(jù)結構之帶頭結點的單鏈表

    數(shù)據(jù)結構之帶頭結點的單鏈表

    單鏈表是一種鏈式存取的數(shù)據(jù)結構,用一組地址任意的存儲單元存放線性表中的數(shù)據(jù)元素。鏈表中的數(shù)據(jù)是以結點來表示的,每個結點的構成:數(shù)據(jù)域(數(shù)據(jù)元素的映象)?+?指針域(指示后繼元素存儲位置),元素就是存儲數(shù)據(jù)的存儲單元,指針就是連接每個結點的地址數(shù)據(jù)
    2023-07-07
  • C語言實現(xiàn)堆的簡單操作的示例代碼

    C語言實現(xiàn)堆的簡單操作的示例代碼

    堆(heap)是計算機科學中一類特殊的數(shù)據(jù)結構的統(tǒng)稱。堆通常是一個可以被看做一棵樹的數(shù)組對象。本文介紹了C語言中堆的一些簡單操作,需要的可以參考一下
    2022-11-11
  • VC編程控件類HTControl之CHTGDIManager GDI資源管理類用法解析

    VC編程控件類HTControl之CHTGDIManager GDI資源管理類用法解析

    這篇文章主要介紹了VC編程控件類HTControl之CHTGDIManager GDI資源管理類用法解析,需要的朋友可以參考下
    2014-08-08
  • C++游戲教程基本技巧之隨機化詳解

    C++游戲教程基本技巧之隨機化詳解

    在小游戲的制作中時常常會要用到隨機數(shù),這篇文章就來和大家談談C++中這個所謂的“隨機”。文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-11-11
  • C++?spdlog?日志庫使用小結

    C++?spdlog?日志庫使用小結

    spdlog是目前?C++?生態(tài)中性能最高(每秒可處理數(shù)百萬條消息)、使用最廣泛的現(xiàn)代日志庫之一,下面就來詳細的介紹下spdlog?日志庫使用,感興趣的可以了解一下
    2026-06-06

最新評論

永靖县| 舟曲县| 大丰市| 桐庐县| 修水县| 松阳县| 松阳县| 滕州市| 克山县| 达尔| 达尔| 盖州市| 定日县| 普格县| 任丘市| 浙江省| 肥城市| 通化市| 东方市| 资溪县| 安多县| 海淀区| 北流市| 台北县| 华宁县| 万山特区| 凌源市| 桂阳县| 青海省| 临朐县| 永清县| 安西县| 镇康县| 丘北县| 安福县| 德安县| 梁河县| 万盛区| 宝清县| 冕宁县| 钦州市|