容器类型独特的项目

时间:2011-05-26 17:44:01

标签: c++

我认为题目标题有点模糊,所以至少感谢阅读;-)

我的情况是这样的:我有一组类CommandACommandB,...来自一个共同的纯抽象基类ICommand。现在我需要将这些派生类的实例保存在某种容器中,但需要考虑的是,任何时候都应该允许容器内只有一个派生类型。相反,当已经存在的派生类型的项目被插入到集合中时,将替换现有的新实例。

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

我认为这需要在运行时进行某种类型识别。我已经排除了C ++编译器提供的Runtime-Type-Identification,因为我们可能需要在某些时候(但未知)旧的编译器上编译项目。因此,大多数机智的模板技巧也可能不在游戏中。但坦率地说,我仍然非常感谢手工为我的派生类分配一些数字标识符......

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

提前多多谢谢,

·阿尔

6 个答案:

答案 0 :(得分:1)

如果你不能使用模板或RTTI,你可以做这样的事情

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



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


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

您可以将每个类的静态变量的地址用作其typeid

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

数字标识符有什么问题?在基类中创建一个enum,以及为每个对象存储枚举值的成员。然后,使每个子类构造函数将枚举成员设置为适当的值。 (可选)使用枚举值编写operator<operator==,并使用std::set作为容器。

答案 3 :(得分:0)

除非您使用多重继承(您不应该),否则可以访问对象的前4/8字节以获取vfptr(虚函数表指针)。它对每种对象类型都是唯一的。

答案 4 :(得分:0)

一般准则:

  • 如果您不能使用RTTI,那么您可以通过添加一个返回类名的静态方法(或任何其他可用作标识的类型)来识别类。
  • std::set容器可用于存储唯一对象。通常它按存储类型的值进行比较(在我们的比较中,它将通过指针值比较Command对象)。
  • 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;
}

答案 5 :(得分: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或多态 - 事实上,并不要求Command类型甚至来自公共基类。您需要做的就是为Command中的每个command_instance_tracker::command_set_t类型创建条目。

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