请考虑以下(简化)类层次结构和处理函数:
struct msgBase
{
virtual int msgType() const=0;
};
struct msgType1:public msgBase
{
virtual int msgType() const{return 1;}
};
struct msgType2:public msgBase
{
virtual int msgType() const {return 2;}
};
void process(const msgType1& mt1)
{
// processing for message type 1
}
void process(const msgType2& mt2)
{
// processing for message type 2
}
void process(const msgBase& mbase)
{
switch(mbase.msgType())
{
case 1:
process(static_cast<const msgType1&>(mbase));
break;
case 2:
process(static_cast<const msgType2&>(mbase));
break;
}
}
在集成设计中,msgBase将被赋予一个虚拟的“进程”方法,以避免需要迭代类型。
如果不可能或不希望修改任何类,是否有任何替代方法可以迭代这些类型?
我已经尝试过装饰器/工厂模式,其中类的并行层次结构封装了给定的类,并实现了必要的虚函数,但这导致了大量的样板,并且工厂函数仍然需要迭代类型!
我可以用一系列dyamic_cast替换switch语句,但仍然存在相同的缺点。
答案 0 :(得分:2)
按照西蒙的要求,这就是CRTP的意思:
typedef <class Derived>
struct msgBase
{
virtual void process(){
// redirect the call to the derived class's process()
static_cast<Derived*>(this) -> process();
};
struct msgType1:public msgBase<msgType1>
{
void process(){
// process as per type-1
}
};
struct msgType2:public msgBase<msgType1>
{
void process(){
// process as per type-2
}
};
这里发生了什么?考虑这种情况:
msgBase* msg = new msgType1();
msg->process();
通常(没有CRTP)这只会调用msgBase::process()
。但现在,msgBase
“知道”使用模板msgType1
,因此在编译时将其重定向到msgType1::process
。
答案 1 :(得分:1)
这样的事情可行:
这些类用于自动进行转换:
struct dispatcher_base {
virtual void process(const msgBase&) = 0;
};
template <class T>
struct dispatcher_impl : dispatcher_base {
void process(const msgBase& b) override {
::process(static_cast<const T&>(b));
}
};
我们会将它们存储在map
:
auto g_table = std::map<int, std::unique_ptr<dispatcher_base>>{};
但现在你必须在某个地方初始化这个表:
template <class T>
void register_msg() {
g_table[T{}.msgType()].reset(new dispatcher_impl<T>{});
}
...
register_msg<msgType1>();
register_msg<msgType2>();
您可以向assert
添加register_msg
,以确保msgType
是唯一的。
您的process
功能如下所示:
void process(const msgBase& b) {
assert(g_table.find(b.msgType()) != g_table.end());
g_table[b.msgType()]->process(b);
}
当然,您可以将assert
替换为任何其他逻辑。
答案 2 :(得分:1)
如果无法修改类,则可以使用装饰器来获取多态类型。
struct DecorBase {
DecorBase(msgBase& b) : b_(b) {}
virtual ~DecorBase() {}
virtual void process() = 0;
msgBase& b_;
};
struct DecorType1 : public DecorBase {
DecorType1(msgType1& t1) : DecorBase(t1) {}
void process() override {
std::cout << "Processing Type 1" << std::endl;
}
};
struct DecorType2 : public DecorBase {
DecorType2(msgType2& t2) : DecorBase(t2) {}
void process() override {
std::cout << "Processing Type 2" << std::endl;
}
};
并像这样使用它:
msgType1 t1;
msgType2 t2;
DecorType1 dt1(t1); // Wrap objects in respective decorator.
DecorType2 dt2(t2);
DecorBase& base = dt2;
base.process(); // Uses polymorphism to call function in derived type.
这将要求你为每个派生类型编写一个装饰器,但至少你不必在函数调用期间迭代所有类型。