【问题标题】:C++ collection with variable types keyed by type具有按类型键控的变量类型的 C++ 集合
【发布时间】:2013-08-27 18:44:59
【问题描述】:

我正在为一堆对象的延迟初始化创建一个类(不是完全通用的,都在我的控制之下)。每种类型只存在一个对象。我已经使用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&lt;...&gt;() 中的循环并以某种方式按类型键入?我希望有恒定时间访问而不是线性时间,而当前方法无法利用可用于查找的类型信息。

为了对我的想法做一点额外的解释,我可能会在 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);
    }
 }

【问题讨论】:

  • 这是一组封闭的类型,还是开放的?换句话说,在定义包含句柄的数据结构时,您是否知道它应该包含的所有可能类型?
  • @dhavenith 什么时候?它通常在编译时关闭,但我不希望在客户端列表中硬编码,如果这是你所得到的:它会引入我宁愿避免的循环依赖问题。

标签: c++ templates containers


【解决方案1】:

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

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

【讨论】:

  • 试试这个...知道typeid 的支持范围有多广吗?
  • typeid 在 C++ 标准中进行了描述。你需要#include &lt;typeinfo&gt;
  • 可能有一个更聪明的答案,它基于 C++.11 元组的工作原理。我现在没有时间把它全部解决,但我会在以后处理它。在此之前,请考虑如何编写模板函数来迭代 boost::tuple 以查找正确的成员。
【解决方案2】:

如果您希望一个类只实例化一次,您可能正在寻找设计模式singleton

维基百科定义:

在软件工程中,单例模式是一种将类的实例化限制为一个对象的设计模式。当需要一个对象来协调整个系统的动作时,这很有用。这个概念有时被推广到当只有一个对象存在时运行效率更高的系统,或者将实例化限制为一定数量的对象。该术语来自单例的数学概念。

关于单例的其他链接

C++ Singleton design pattern

Singleton: How should it be used

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

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

【讨论】:

  • 是的,我知道这类似于单例模式。但是,识别该模式实际上并没有告诉我如何实现它!
  • 我给了你一些与它实现相关的链接。然后您不再需要收集,您只需执行 MyType::instance() 即可获取对象,并且您确定它只实例化一次。
  • 我不想使用静态初始化器:我可能有多个对象的实例浮动,但在某个概念障碍内只有一个实例存在。例如,客户端不是线程安全的,因此每个线程将存在一个 ObjectProvider。
  • 好的,那么我认为 map 和 typeid 的想法(这是标准的 C++98/11,因此应该在所有编译器上都可用)是最好的选择之一
【解决方案3】:

如果担心 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 字段上发生冲突。

【讨论】:

    【解决方案4】:

    如果您想要恒定时间访问,您可以创建自己的 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。

    【讨论】:

      猜你喜欢
      • 2021-10-13
      • 2017-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-11
      • 1970-01-01
      • 1970-01-01
      • 2012-09-04
      相关资源
      最近更新 更多