具有按类型键入的变量类型的C ++集合

时间:2013-08-27 18:44:59

标签: c++ templates containers

我正在为一堆对象的懒惰初始化创建一个类(不是完全通用的,都在我的控制之下)。每种类型只有一个对象存在。我已经使用std::vectorboost::any进行了线性时间实施。希望它能更好地了解我在说什么。

我可以假设我想要访问的所有对象都有

typedef boost::shared_ptr<CLASSNAME> HandleType

在他们的定义中,它们都有一个构造函数,它通过引用获取ObjectProvider。

class ObjectProvider {
    typedef std::vector<boost::any> ContainerType;
    ObjectProvider() : container() {}

public:

    // this is where the relevant method is
    template <class TElementType>
    typename TElementType::HandleType get() {
        for (ContainerType::iterator it = container.begin(); it != container.end(); ++it) {
            try {
                return boost::any_cast<typename TElementType::HandleType>(*it);
            } catch (boost::bad_any_cast &) {}
        }
        // failed to find it so create it
        TElementType::HandleType handle = boost::make_shared<TElementType>(*this);
        container.push_back(handle);
        return handle;            
    }

private:
    ContainerType container;
};


// ----- FOR TESTING -----
class SomeObject {
public:
     SomeObject(ObjectProvider &) {}
     typedef boost::shared_ptr<SomeObject> HandleType;
};

int main(int argc, char** argv) {
    ObjectProvider provider;

    // expensive creation is done here
    SomeObject::HandleType obj1 = provider.get<SomeObject>();

    // expensive creation is not re-done
    SomeObject::HandleType obj2 = provider.get<SomeObject>();
    assert (obj1 == obj2); // pointers should point to the same object

}

一些动机:许多这些对象(它们是各种服务的客户端)需要创建各种类型的其他客户端,但我不希望每次都重新创建它们。所以这个类的目标是提供一种缓存已经创建的客户端的方法。

这是我的问题:

  • 有更好的方法吗?

  • 特别是,有没有办法避免get<...>()中的循环,并以某种方式按类型键入?我希望有恒定时间访问而不是线性时间,并且当前方法无法使用可用于查找的类型信息。

只是为了对我的想法做一点补充说明,我可能会在Java中做这样的事情:

Map<Class<?>, Object> objMap;
public <T> T get(Class<T> class) {
    Object obj = objMap.get(class);
    if (obj != null) {
        return (T)obj;
    } else {
        T newObj = ;// fiddle with java reflection to create a new instance
        objMap.put(class, newObj);
    }
 }

4 个答案:

答案 0 :(得分:1)

如果每种类型只有一种,那么您可以使用typeid提取表示该类型的字符串,并将其用作mapunordered_map的键。 / p>

    //...
    typedef std::map<std::string, boost::any> ContainerType;
    //...

    template <class TElementType>
    typename TElementType::HandleType get() {
        std::string name = typeid(TElementType).name();

        ContainerType::iterator it = container.find(name);
        if (it != container.end()) {
            try {
                return boost::any_cast<typename TElementType::HandleType>(it->second);
            } catch (boost::bad_any_cast &) {}
        }
        // failed to find it so create it
        TElementType::HandleType handle = boost::make_shared<TElementType>(*this);
        container[name] = handle;
        return handle;            
    }

答案 1 :(得分:0)

如果您希望课程仅实例一次,您可能正在寻找设计模式 singleton

维基百科定义:

  

在软件工程中,单例模式是一种设计模式,它将类的实例化限制为一个对象。当需要一个对象来协调整个系统的操作时,这非常有用。该概念有时被推广到当仅存在一个对象时更有效地操作的系统,或者将实例化限制为特定数量的对象的系统。该术语来自单身人士的数学概念。

关于单身人士的其他链接

C++ Singleton design pattern

Singleton: How should it be used

Can any one provide me a sample of Singleton in c++?

你可以在互联网上找到更多关于它的解释。

答案 2 :(得分:0)

如果您担心typeid()支持,您也可以自己添加类索引。

class SomeObject {
public:
    SomeObject(ObjectProvider &) {}
    typedef boost::shared_ptr<SomeObject> HandleType;
    static const int ClassIndex = 0;
};

并且对象提供程序变为

class ObjectProvider {
    typedef std::vector<boost::any> ContainerType;

public:
    ObjectProvider() : container() {}

    // this is where the relevant method is
    template <class TElementType>
    typename TElementType::HandleType get() {
        int idx = TElementType::ClassIndex;
        if (container.size() <= idx) {
            container.resize(idx + 1);
        }

        // Check if object exists
        if (container[idx].empty()) {
            typename TElementType::HandleType handle = boost::make_shared<TElementType>(*this);
            container[idx] = handle;
        }

        return boost::any_cast<typename TElementType::HandleType>(container[idx]);
    }

private:
    ContainerType container;
};

在缺点方面,您必须确保不同的类不会在ClassIndex字段上发生冲突。

答案 3 :(得分:0)

如果您想要持续时间访问,可以创建自己的type-id:

class Registry
{
private:
    static int get_id()
    {
        static int id = 0;
        return id++;
    }

    template< typename T>
    static int get_type_id()
    {
        const static int id = get_id();
        return id;
    }
    std::vector<HandleType> handles;
    HandleType make_handle( const Registry&r)
    {
        // do what you need to do to create a new handle.
    }
public:
    template<typename T>
    HandleType get()
    {
        int id = get_type_id<T>();
        if (id + 1 > handles.size())        
        {
            handles.resize( id + 1, HandleType());
        }
        if (!handles[id])
        {
            handles[id] = make_handle( *this);
        }
        return handles[id];
    }

};

编辑:我发现我基本上给出了与umlum相同的答案,但这个答案还会尝试自动为每种类型创建一个数字ID。