我认为题目标题有点模糊,所以至少感谢阅读;-)
我的情况是这样的:我有一组类CommandA
,CommandB
,...来自一个共同的纯抽象基类ICommand
。现在我需要将这些派生类的实例保存在某种容器中,但需要考虑的是,任何时候都应该允许容器内只有一个派生类型。相反,当已经存在的派生类型的项目被插入到集合中时,将替换现有的新实例。
此外,需要根据其类型从集合中删除项目。
我认为这需要在运行时进行某种类型识别。我已经排除了C ++编译器提供的Runtime-Type-Identification,因为我们可能需要在某些时候(但未知)旧的编译器上编译项目。因此,大多数机智的模板技巧也可能不在游戏中。但坦率地说,我仍然非常感谢不手工为我的派生类分配一些数字标识符......
我很感激每一个关于这个问题的暗示。
提前多多谢谢,
·阿尔
答案 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)
一般准则:
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
类型创建条目。
如果您有任何问题,请随时询问。