C++ 唯一项目类型的容器

C++ 唯一项目类型的容器,c++,C++,我知道问题的标题有点模糊,所以至少感谢您阅读;-) 我的情况是:我有一组类CommandA,CommandB。。。派生自一个普通的纯抽象基类ICommand。现在,我需要将这些派生类的实例保存在某种容器中,但使用restriction,即在任何时候都只允许每个派生类型中的一个在容器中。相反,当已存在派生类型的项插入到集合中时,将出现用新实例替换现有实例的情况 此外,还需要根据项目的类型从集合中删除项目 我假设这在运行时需要某种类型的标识。我排除了由C++编译器提供的运行时类型标识,因为我们可能需

我知道问题的标题有点模糊,所以至少感谢您阅读;-)

我的情况是:我有一组类
CommandA
CommandB
。。。派生自一个普通的纯抽象基类
ICommand
。现在,我需要将这些派生类的实例保存在某种容器中,但使用restriction,即在任何时候都只允许每个派生类型中的一个在容器中。相反,当已存在派生类型的项插入到集合中时,将出现用新实例替换现有实例的情况

此外,还需要根据项目的类型从集合中删除项目

我假设这在运行时需要某种类型的标识。我排除了由C++编译器提供的运行时类型标识,因为我们可能需要在某个点上编译(未知的)旧编译器上的项目。因此,大多数机智的模板技巧也可能退出游戏。但坦率地说,我仍然非常感谢不要手动为我的派生类指定一些数字标识符

我很感激关于这个问题的每一个暗示

提前多谢了


Arne

向ICommand添加一个函数,该函数返回派生类的标识符。它不需要是数字标识符,它可以是字符串、GUID或任何其他方便您的东西


使用std::map包含指向类对象的指针,使用标识符作为键。

数字标识符有什么问题?在基类中创建一个
enum
,以及一个存储每个对象的枚举值的成员。然后,让每个子类构造函数将枚举成员设置为适当的值。如果不能使用模板或RTTI,可以选择编写一个
操作符,您可以这样做

          class ICommand
    {
        virtual void *getStaticId() = 0;
    }



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


    int foo;
    void* CommandB::getStaticId()
    {
        return &foor;
    }

您可以使用每个类的静态变量的地址作为其typeid,除非您使用多重继承(不应该),否则您可以访问对象的前4/8个字节以获得vfptr(虚拟函数表指针)。它对于每种对象类型都是唯一的。

一般准则:

  • 如果不能使用RTTI,那么可以通过添加一个返回类名(或任何其他可用作标识的类型)的静态方法来标识类
  • std::set
    容器可用于存储唯一的对象。通常,它通过存储类型的值进行比较(在我们的示例中,它将通过指针值比较命令对象)
  • std::set
    也可以使用自定义比较器。我们需要一个通过类名比较对象的方法
下面是一个工作代码示例:

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
类I命令
{
公众:
ICommand(const char*inClassName):mClassName(inClassName){}
虚拟~ICommand(){}
const std::string&getClassName()const{return mClassName;}
私人:
std::字符串mClassName;
};
类命令A:公共ICommand
{
公众:
静态常量char*ClassName(){返回“CommandA”;}
CommandA():ICommand(ClassName()){}
};
类命令B:公共ICommand
{
公众:
静态常量char*ClassName(){return“CommandB”;}
CommandB():ICommand(ClassName()){}
};
结构比较器
{
布尔运算符()(常量ICommand*lhs,常量ICommand*rhs)常量
{返回lhs->getClassName()getClassName();}
};
int main()
{
typedef std::set命令;
命令;
//添加
命令。插入(新命令a);
std::cout因为(根据您的评论)您提前知道所有的
ICommand
衍生工具,所以可以使用以下方法轻松实现:

#包括
#包括
#包括
#包括
//存根ICommand和继承链
结构ICommand{virtual~ICommand()=0;};
ICommand::~ICommand(){}
结构CommandA:ICommand{~CommandA(){};
结构CommandB:ICommand{~CommandB(){};
struct CommandC:ICommand{~CommandC(){};
//实际实现,根据需要重命名
类命令\实例\跟踪器
{
typedef boost::fusion::set<
boost::可选,
boost::可选,
boost::可选
>命令集;
静态命令集命令集;
公众:
模板
静态命令&get_实例()
{
使用boost::fusion::at_键;
使用boost::可选;
如果(!at_键(命令设置))
抛出std::runtime_错误(“没有指定命令类型的实例”);
返回*at_键(命令设置);
}
模板
静态void set_实例(命令常量和实例)
{
使用boost::fusion::at_键;
使用boost::可选;
at_键(命令设置)=实例;
}
};
命令\实例\跟踪器::命令\集合\命令\实例\跟踪器::命令\集合\命令;
//用法示例
int main()
{
//抛出运行时错误,因为从未设置CommandA实例
CommandA&a=command_instance_tracker::get_instance();
{
b-b1;
//存储CommandB实例
命令\实例\跟踪器::设置\实例(b1);
}
//获取存储的CommandB实例,该实例是从b1复制的
CommandB&b2=命令\实例\跟踪器::获取\实例();
}
请注意,这种方法没有使用RTTI或多态性——事实上,
命令
类型甚至不需要从公共基类派生。您所需要做的就是为每个
命令
键入
命令实例跟踪::命令集


如果您有任何问题,请随时提问。

Resources:,您知道编译时派生的
命令
类型吗?@ildjarn I fact I知道。此外,所有命令类都单独驻留在“我的”中模块。这完全可以在没有RTTI的情况下完成。我将在几分钟后发布一个答案。这将是一个非常依赖于编译器的解决方案。对我来说太脆弱了。@Mark:Name
#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>();
}