类型唯一的容器

问题描述

| 我知道标题的问题有点含糊,所以至少感谢您阅读它;-) 我的情况是这样的:我有一组类
CommandA
CommandB
...,它们是从一个普通的纯抽象基类
ICommand
派生而来的。现在,我需要将这些派生类的实例保存在某种容器中,但要放心,任何时候都只能在容器内允许每个派生类型之一。相反,当将已经存在的派生类型的项插入到集合中时,将会用新实例替换现有实例。 此外,需要根据项目的类型从集合中删除项目。 我认为这将需要在运行时进行某种类型的标识。我已经排除了C ++编译器提供的Runtime-Type-Identification,因为在某些时候我们可能需要在(但未知)较旧的编译器上编译项目。因此,大多数机智的模板技巧也可能不在游戏之列。但坦率地说,我仍然非常希望不要手动为派生类分配一些数字标识符... 对于这个问题的每一个提示我都表示感谢。 非常感谢 Arne     

解决方法

如果您无法使用模板或RTTI,则可以执行以下操作
          class ICommand
    {
        virtual void *getStaticId() = 0;
    }



    int bar;
    void* CommandA::getStaticId()
    {
        return &bar;
    }


    int foo;
    void* CommandB::getStaticId()
    {
        return &foor;
    }
您可以将每个类的静态变量的地址用作其typeid     ,向ICommand添加一个函数,该函数返回派生类的标识符。它不必是数字标识符,可以是字符串,GUID或其他任何对您方便的东西。 使用std :: map将标识符用作键,以包含指向类对象的指针。     ,数字标识符有什么问题?在基类中加上一个存储每个对象的枚举值的成员,使之等于4。然后,使每个子类构造函数将枚举成员设置为适当的值。 (可选)使用枚举值分别编写ѭ5和ѭ6,并将use7用作容器。     ,除非您使用多重继承(不应这样做),否则您可以访问对象的前4/8个字节以获得vfptr(虚拟函数表指针)。它对于每种对象类型都是唯一的。     ,一般准则: 如果您不能使用RTTI,则可以通过添加返回类名(或可用作标识的任何其他类型)的静态方法来标识类。
std::set
容器可用于存储唯一的对象。通常,它按存储类型的值进行比较(在本例中,它将按指针值对Command对象进行比较)。 ѭ7也可以使用自定义比较器。我们需要一个通过对象的类名比较对象的对象。 这是一个工作代码示例:
#include <boost/bind.hpp>
#include <algorithm>
#include <iostream>
#include <set>
#include <stdexcept>
#include <string>

class ICommand
{
public:
    ICommand(const char * inClassName) : mClassName(inClassName) { }

    virtual ~ICommand() {}

    const std::string & getClassName() const { return mClassName; }

private:
    std::string mClassName;
};


class CommandA : public ICommand
{
public:
    static const char * ClassName() { return \"CommandA\"; }

    CommandA() : ICommand(ClassName()) { }
};


class CommandB : public ICommand
{
public:
    static const char * ClassName() { return \"CommandB\"; }

    CommandB() : ICommand(ClassName()) { }
};


struct Comparator
{
    bool operator()(const ICommand * lhs,const ICommand * rhs) const
    { return lhs->getClassName() < rhs->getClassName(); }
};


int main()
{
    typedef std::set<ICommand*,Comparator> Commands;
    Commands commands;

    // Add A
    commands.insert(new CommandA);
    std::cout << \"commands.size after adding CommandA: \" << commands.size() << std::endl;

    // Add A again,overwrites the first A
    commands.insert(new CommandA);
    std::cout << \"commands.size after adding a second CommandA: \" << commands.size() << std::endl;

    // Add B
    commands.insert(new CommandB);
    std::cout << \"commands.size after adding CommandB: \" << commands.size() << std::endl;

    // Find B
    Commands::iterator it = std::find_if(commands.begin(),commands.end(),boost::bind(&ICommand::getClassName,_1) == CommandB::ClassName());
    if (it == commands.end())
    {
        throw std::logic_error(\"Could not find CommandB in the set.\");
    }

    // Print found object name
    ICommand * theFoundCommand = *it;
    std::cout << \"Found a command,it\'s name is: \" << theFoundCommand->getClassName() << std::endl;

    // Erase B
    commands.erase(it);

    std::cout << \"commands.size after removing CommandB: \" << commands.size() << std::endl;

    return 0;
}
    ,由于(根据您的评论)您提前知道所有
ICommand
导数,因此可以使用Boost.Fusion轻松实现:
#include <stdexcept>
#include <boost/optional.hpp>
#include <boost/fusion/include/set.hpp>
#include <boost/fusion/include/at_key.hpp>

// stub ICommand and inheritance chain
struct ICommand { virtual ~ICommand() = 0; };
ICommand::~ICommand() { }
struct CommandA : ICommand { ~CommandA() { } };
struct CommandB : ICommand { ~CommandB() { } };
struct CommandC : ICommand { ~CommandC() { } };

// actual implementation,rename as you see fit
class command_instance_tracker
{
    typedef boost::fusion::set<
        boost::optional<CommandA>,boost::optional<CommandB>,boost::optional<CommandC>
    > command_set_t;

    static command_set_t command_set_;

public:
    template<typename CommandT>
    static CommandT& get_instance()
    {
        using boost::fusion::at_key;
        using boost::optional;
        if (!at_key<optional<CommandT> >(command_set_))
            throw std::runtime_error(\"no instance for specified command type\");
        return *at_key<optional<CommandT> >(command_set_);
    }

    template<typename CommandT>
    static void set_instance(CommandT const& instance)
    {
        using boost::fusion::at_key;
        using boost::optional;
        at_key<optional<CommandT> >(command_set_) = instance;
    }
};
command_instance_tracker::command_set_t command_instance_tracker::command_set_;

// example of usage
int main()
{
    // throws runtime_error,as CommandA instance was never set
    CommandA& a = command_instance_tracker::get_instance<CommandA>();

    {
        CommandB b1;
        // stores the CommandB instance
        command_instance_tracker::set_instance(b1);
    }

    // gets stored CommandB instance,which was copied from b1
    CommandB& b2 = command_instance_tracker::get_instance<CommandB>();
}
请注意,此方法完全不使用RTTI或多态性-实际上,不需要requirement13ѭ类型甚至可以从通用基类派生。您要做的就是为
command_instance_tracker::command_set_t
中的每种
Command
类型创建条目。 随时询问您是否有任何问题。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...