gpt4 book ai didi

c++ - 从没有虚函数的类继承的最佳方法

转载 作者:行者123 更新时间:2023-12-02 09:52:13 26 4
gpt4 key购买 nike

我正在尝试实现一些事件处理。
事件有不同类型:integerChangedEvent,boolChangedEvent,stringChangedEvent等。
每个事件都包含一些相同的信息,例如:std::string settingsName,std::string containerName ...
但是,这些不同的事件类型中的每一个也都包含一些此事件类型所独有的信息: int double std::string ... newValue和oldValue。
我不复制相同代码数千次的想法是创建一个名为SettingsEvent的基类。
此类应保存所有事件类型也将保存且相同的信息(请参见上文“settingsName,containerName”),并导致这些信息的设置者和获取者。
所有其他事件都可以继承此基类并添加自己的成员/方法。
到目前为止,一切都很好。
但是C++(11)确实允许我从没有虚拟方法的类继承,但是当没有至少一种方法定义为虚拟时,它不允许我从基类到派生类进行dynamic_cast。
但我不想允许所有这些方法都可以覆盖。
我能做什么?是否有一个说明符可以让我强制转换非虚拟类?
为了更好地理解,下面是一些代码:

class SettingsEvent
{
public:
std::string getSettingsName() const;
std::string getSettingsContainerName() const;
// some more methods I don't want to write down know... ;)
protected:
SettingsEvent(); //protected constructor, to ensure nobody creates an object of this base class
private:
std::string m_settingsName;
std::string m_settingsContainerName;
// some more members I also don't want to write down know...
};

class IntegerChangedEvent : public SettingsEvent
{
public:
IntegerChangedEvent(); //public constructor, it is allowed to create an object of this class
int getNewValue() const;
int getOldValue() const;
//also here are more methods I don't want to list
private:
int m_newValue;
int m_oldValue;
//also more members I don't want to list
};
在代码的另一方面,我想将事件传递给方法。在该方法中,我想将其转换为IntegerChangedEvent:
void handleEvent(SettingsEvent* event)
{
//to be honest, the event itself knows what kind of event it is (enum) but lets say it is an IntegerChangedEvent to keep it simple
IntegerChangedEvent* intEvent = dynamic_cast<IntegerChangedEvent*>(event);

if(intEvent)
{
//do stuff
}
}
错误消息是:“C2683:'dynamic_cast':'SettingsEvent'不是多态类型

最佳答案

好的,因此事件知道它是什么类型。

 switch (event->type)
{
case IntegerChangedEventType: {
IntegerChangedEvent* ievent = static_cast<IntegerChangedEventType*>(event);
handleIntegerChangedEvent(ievent);
break;
}
case StringChangedEventType: {
StringChangedEvent* sevent = static_cast<StringChangedEventType*>(event);
handleStringChangedEvent(sevent);
break;
}
// ... etc etc etc
}
(您可以使用静态或动态类型转换;动态类型转换显然至少需要一个虚函数;如果您确定事件不关乎其类型,则静态类型转换完全可以)。
恭喜我们!我们刚刚重新实现了虚函数调度,但是效果很差,但是我们自己完成了所有这些工作,而没有听过所有那些讨厌的OO伪专家,我们可以为这一巨大成就感到自豪!虚拟坏,非虚拟好!
我们本来可以写
event->handle();
并称之为一天,但是那有什么乐趣呢?
好的,您说的是,但是 Activity 实际上并不知道如何处理。这只是一些愚蠢的值(value)观集合。因此, event->handle();不可行。为了实现它,我们必须引入各种应用程序业务逻辑,可能会创建循环依赖关系。现在怎么办?
输入 visitor。这是专门为处理这种情况而设计的设计模式。它将虚拟调度机制与通过该机制调用的实际逻辑解耦。虚拟调度是 SettingsEvent类的职责。逻辑是 EventVisitor类的责任。因此 EventVisitor知道如何处理各种事件,而 SettingsEvent告诉它现在要处理什么。总体流程与我们最初的开关案例代码没有太大不同,样板代码也没有减少,但是代码更加结构化并且易于修改。您无法添加新的事件类型,而忘记更新旧的处理程序。编译器会大喊大叫。
 class EventVisitor
{
virtual void handle(IntegerChangedEvent& ev) = 0;
virtual void handle(StringChangedEvent& ev) = 0;
};

class SettingsEvent
{
virtual void accept (EventVisitor& vis) = 0;
};

class IntegerChangedEvent: public SettingsEvent
{
void accept (EventVisitor& vis) override { vis.handle(*this); }
};

class StringChangedEvent: public SettingsEvent
{
void accept (EventVisitor& vis) override { vis.handle(*this); }
};

// actual event handling logic
class AppEventHandler : public EventVisitor
{
void handle(IntegerChangedEvent& ev) override { /* specific logic */ }
void handle(StringChangedEvent& ev) override { /* specific logic */ }
};
好的,您说的是,但是访客已经有几十年了,难道我们不拥有更现代,更 slim ,卑鄙,时髦的,对15英里半径不宜的1990年代吗?当然可以! C++ 17为我们带来了 std::variant std::visit ,这与旧的访问者模式基本相同,只是 std::variant本身处理的部分而不是它持有的任何 Event的部分。您将所有 SettingsEvent子类放入 variant中,它知道接下来要做什么。几乎不需要任何虚拟东西。
using AnyEvent = std::variant<IntegerChangedEvent, StringChangedEvent, ...>;

AnyEvent event = ...;
std::visit(overloaded
{
[](IntegerChangedEvent& ev) { ... },
[](StringChangedEvent& ev) { ... },
}, event);

因此,从史前的Fortran风格的类型分发到基本OO到高级OO,再回到Fortran风格,到现在,我们有了一个完整的圈子,现在有了更多的风格。选择您喜欢的任何一个。

关于c++ - 从没有虚函数的类继承的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63686677/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com