欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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ù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • 2048小游戲C語言實現(xiàn)代碼

    2048小游戲C語言實現(xiàn)代碼

    這篇文章主要為大家詳細介紹了2048小游戲C語言實現(xiàn)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • C語言 基本語法示例講解

    C語言 基本語法示例講解

    本篇文章主要講解C語言 基本語法,這里提供簡單的示例和代碼來詳細講解C語言的基本語法,開始學習C語言的朋友可以看一下
    2016-08-08
  • GetChar緩存機制深入剖析

    GetChar緩存機制深入剖析

    以下是對GetChar緩存機制進行了詳細的介紹,需要的朋友可以過來參考下
    2013-09-09
  • C語言直接插入排序算法

    C語言直接插入排序算法

    大家好,本篇文章主要講的是C語言直接插入排序算法,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • C語言二叉樹的非遞歸遍歷實例分析

    C語言二叉樹的非遞歸遍歷實例分析

    這篇文章主要介紹了C語言二叉樹的非遞歸遍歷,包括了先序遍歷、中序遍歷與后序遍歷,需要的朋友可以參考下
    2014-09-09
  • C++超詳細實現(xiàn)二叉樹的遍歷

    C++超詳細實現(xiàn)二叉樹的遍歷

    本章將會詳細講解二叉樹遍歷的四種方式,分別為前序遍歷、中序遍歷、后續(xù)遍歷和層序遍歷。在學習遍歷之前,會先帶大家回顧一下二叉樹的基本概念
    2022-05-05
  • C語言中求余運算符的使用解讀

    C語言中求余運算符的使用解讀

    這篇文章主要介紹了C語言中求余運算符的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 重構-C++實現(xiàn)矩陣的簡單實例

    重構-C++實現(xiàn)矩陣的簡單實例

    下面小編就為大家?guī)硪黄貥?C++實現(xiàn)矩陣的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • 解析C++編程中的繼承方面的運用

    解析C++編程中的繼承方面的運用

    這篇文章主要介紹了解析C++編程中的繼承方面的運用,是C++入門學習中的基礎知識,需要的朋友可以參考下
    2015-09-09
  • C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實例

    C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實例

    這篇文章主要介紹了C++中靜態(tài)成員函數(shù)訪問非靜態(tài)成員的實例的相關資料,需要的朋友可以參考下
    2017-07-07

最新評論