主题:能不能在同一个STL容器比如VECTOR中存取不同类型的对象
强强
[专家分:4740] 发布于 2010-08-14 14:51:00
能不能在同一个STL容器比如VECTOR中存取不同类型的对象,如果能的话怎么处理呢?
回复列表 (共5个回复)
沙发
Screenager [专家分:840] 发布于 2010-08-14 21:25:00
用容器存储union
板凳
强强 [专家分:4740] 发布于 2010-08-14 22:04:00
谢谢,不过我不知道您这种办法能不能让对象正确构造和析构?您觉得呢?
3 楼
eastcowboy [专家分:25370] 发布于 2010-08-15 02:31:00
语法上讲,std::vector内部各个元素的类型都应该相同。但这也不会让人奇怪——C/C++的程序员都应该习惯这一点。一个数组中所有元素的类型都应该相同,std::vector在这一方面并不比数组做得更好。
Screenager提出了一种解决方案,用union,其实很多时候大家就是这么做的。不过,union也有它的问题,比如,一个union内部不能放入std::string。
另一个方案是用指针。比如std::vector<void*>,里面可以放任何东西了。假如要放进去的数据类型都是从某一个基类继承的话,就可以用基类的指针,而不是void指针了,这样会安全一点,也方便一点。如果希望做得更安全一些,可以用shared_ptr代替原本的指针。
更进一步,可以定义一种数据类型,它内部可以容纳任何数据类型,并且能够安全的处理构造和析构(实际上就是采用指针来实现)。然后再定义这种类型的数组(或者vector),即可实现在一个容器中保存多种类型不同的对象。boost库里面有一份现成的实现,叫做boost::any。楼主有兴趣的话可以看看。
示例代码:
[code=c]#include <typeinfo>
#include <string>
#include <vector>
// 数据保存基类
class DataHolderBase {
public:
DataHolderBase(const void* ptr, const type_info& type)
: m_ptr(ptr)
, m_type(type)
{
}
virtual ~DataHolderBase() {
}
virtual DataHolderBase* Clone() = 0;
const void* GetPtr() {
return m_ptr;
}
const type_info& GetType() {
return m_type;
}
private:
const void* m_ptr;
const type_info& m_type;
};
// 数据保存类,保存类型为T的数据
template <typename T>
class DataHolder : public DataHolderBase {
public:
DataHolder(const T& t)
: DataHolderBase(&m_t, typeid(T))
, m_t(t)
{
}
virtual DataHolderBase* Clone() {
return new DataHolder<T>(m_t);
}
private:
T m_t;
};
// 任意类型,可以容纳各种数据
class AnyType {
public:
template <typename T>
friend const T& AnyCast(const AnyType& any);
AnyType() {
m_pDataHolder = 0;
}
AnyType(const AnyType& copy) {
if (copy.m_pDataHolder) {
m_pDataHolder = copy.m_pDataHolder->Clone();
} else {
m_pDataHolder = 0;
}
}
~AnyType() {
delete m_pDataHolder;
}
AnyType& operator =(const AnyType& other) {
if (&other == this) {
return *this;
}
DataHolderBase* pNewDataHolder;
if (other.m_pDataHolder) {
pNewDataHolder = other.m_pDataHolder->Clone();
} else {
pNewDataHolder = 0;
}
delete m_pDataHolder;
m_pDataHolder = pNewDataHolder;
return *this;
}
template <typename T>
explicit AnyType(const T& t) {
m_pDataHolder = new DataHolder<T>(t);
}
template <typename T>
AnyType& operator =(const T& t) {
DataHolderBase* pNewDataHolder = new DataHolder<T>(t);
delete m_pDataHolder;
m_pDataHolder = pNewDataHolder;
return *this;
}
private:
DataHolderBase* m_pDataHolder;
};
template <typename T>
const T& AnyCast(const AnyType& any) {
DataHolderBase* pDataHolder = any.m_pDataHolder;
if (!pDataHolder) {
throw "type cast failed";
}
if (pDataHolder->GetType() != typeid(T)) {
throw "type cast failed";
}
return *static_cast<const T*>(pDataHolder->GetPtr());
}
int main() {
AnyType a;
int i;
double d;
std::string s;
a = 10;
i = AnyCast<int>(a); // i == 10
a = 20.5;
d = AnyCast<double>(a); // d == 20.5
a = std::string("Hello, World");
s = AnyCast<std::string>(a); // s == "Hello, World"
AnyType b = a;
b = a;
std::vector<AnyType> v(10);
v[0] = 100;
v[2] = 40.2f;
v[3] = (const char*)"Yes!!";
v[4] = std::string("OK");
v[5] = 500.0;
return 0;
}[/code]
4 楼
JackieRasy [专家分:3050] 发布于 2010-08-15 15:42:00
还是不要开出 boost::any 这样的灵丹了....整不好药效过猛会撑住....
直接用std::vector<boost::shared_ptr<void*> >,不求漂亮就够了。。
5 楼
强强 [专家分:4740] 发布于 2010-08-15 23:25:00
[quote]语法上讲,std::vector内部各个元素的类型都应该相同。但这也不会让人奇怪——C/C++的程序员都应该习惯这一点。一个数组中所有元素的类型都应该相同,std::vector在这一方面并不比数组做得更好。
Screenager提出了一种解决方案,用union,其实很多时候大家就是这么做的。不过,union也有它的问题,比如,一个union内部不能放入std::string。
另一个方案是用指针。比如std::vector<void*>,里面可以放任何东西了。假如要放进去的数据类型都是从某一个基类继承的话,就可以用基类的指针,而不是void指针了,这样会安全一点,也方便一点。如果希望做得更安全一些,可以用shared_ptr代替原本的指针。
更进一步,可以定义一种数据类型,它内部可以容纳任何数据类型,并且能够安全的处理构造和析构(实际上就是采用指针来实现)。然后再定义这种类型的数组(或者vector),即可实现在一个容器中保存多种类型不同的对象。boost库里面有一份现成的实现,叫做boost::any。楼主有兴趣的话可以看看。
示例代码:
[code=c]#include <typeinfo>
#include <string>
#include <vector>
// 数据保存基类
class DataHolderBase {
public:
DataHolderBase(const void* ptr, const type_info& type)
: m_ptr(ptr)
, m_type(type)
{
}
virtual ~DataHolderBase() {
}
virtual DataHolderBase* Clone() = 0;
const void* GetPtr() {
return m_ptr;
}
const type_info& GetType() {
return m_type;
}
private:
const void* m_ptr;
const type_info& m_type;
};
// 数据保存类,保存类型为T的数据
template <typename T>
class DataHolder : public DataHolderBase {
public:
DataHolder(const T& t)
: DataHolderBase(&m_t, typeid(T))
, m_t(t)
{
}
virtual DataHolderBase* Clone() {
return new DataHolder<T>(m_t);
}
private:
T m_t;
};
// 任意类型,可以容纳各种数据
class AnyType {
public:
template <typename T>
friend const T& AnyCast(const AnyType& any);
AnyType() {
m_pDataHolder = 0;
}
AnyType(const AnyType& copy) {
if (copy.m_pDataHolder) {
m_pDataHolder = copy.m_pDataHolder->Clone();
} else {
m_pDataHolder = 0;
}
}
~AnyType() {
delete m_pDataHolder;
}
AnyType& operator =(const AnyType& other) {
if (&other == this) {
return *this;
}
DataHolderBase* pNewDataHolder;
if (other.m_pDataHolder) {
pNewDataHolder = other.m_pDataHolder->Clone();
} else {
pNewDataHolder = 0;
}
delete m_pDataHolder;
m_pDataHolder = pNewDataHolder;
return *this;
}
template <typename T>
explicit AnyType(const T& t) {
m_pDataHolder = new DataHolder<T>(t);
}
template <typename T>
AnyType& operator =(const T& t) {
DataHolderBase* pNewDataHolder = new DataHolder<T>(t);
delete m_pDataHolder;
m_pDataHolder = pNewDataHolder;
return *this;
}
private:
DataHolderBase* m_pDataHolder;
};
template <typename T>
const T& AnyCast(const AnyType& any) {
DataHolderBase* pDataHolder = any.m_pDataHolder;
if (!pDataHolder) {
throw "type cast failed";
}
if (pDataHolder->GetType() != typeid(T)) {
throw "type cast failed";
}
return *static_cast<const T*>(pDataHolder->GetPtr());
}
int main() {
AnyType a;
int i;
double d;
std::string s;
a = 10;
i = AnyCast<int>(a); // i == 10
a = 20.5;
d = AnyCast<double>(a); // d == 20.5
a = std::string("Hello, World");
s = AnyCast<std::string>(a); // s == "Hello, World"
AnyType b = a;
b = a;
std::vector<AnyType> v(10);
v[0] = 100;
v[2] = 40.2f;
v[3] = (const char*)"Yes!!";
v[4] = std::string("OK");
v[5] = 500.0;
return 0;
}[/code][/quote]
嗯,好!!!
我来回复