问题描述
|
我知道标题的问题有点含糊,所以至少感谢您阅读它;-)
我的情况是这样的:我有一组类
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
类型创建条目。
随时询问您是否有任何问题。