回 帖 发 新 帖 刷新版面

主题:能不能在同一个STL容器比如VECTOR中存取不同类型的对象

能不能在同一个STL容器比如VECTOR中存取不同类型的对象,如果能的话怎么处理呢?

回复列表 (共5个回复)

沙发

用容器存储union

板凳

谢谢,不过我不知道您这种办法能不能让对象正确构造和析构?您觉得呢?

3 楼

语法上讲,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 楼

还是不要开出 boost::any 这样的灵丹了....整不好药效过猛会撑住....

直接用std::vector<boost::shared_ptr<void*> >,不求漂亮就够了。。

5 楼

[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]
嗯,好!!!

我来回复

您尚未登录,请登录后再回复。点此登录或注册