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

如何解决从没有虚函数的类继承的最佳方法

我正在尝试实施一些事件处理。 事件有不同类型: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();

并称之为一天,但是那有什么乐趣呢?

好的,您说的是,但该活动实际上并不知道如何处理。这只是一些愚蠢的价值观集合。因此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 */ }
 };

好吧,您说的来访者已经有几十年了,难道我们不拥有更现代,更苗条,卑鄙,时髦的,对15英里半径没有1990年代的东西吗?当然可以! C ++ 17带给我们std::variantstd::visit,这与旧的访客模式基本相同,只有 what 部分由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风格,到现在,我们有了一个完整的圈子,现在有了更多的风格。选择任意一个。

,

如果您要继承一个类,则应该使用虚拟析构函数来创建一个vtable,然后再进行动态转换。

本着“更喜欢包含而不是继承”的精神,为什么不简单地在您的SettingsEvent中包含IntegerChangedEvent?同样,本着“使用继承使相似的事物表现不同”和“使用模板对不同类型进行相同的事物”的精神,您不能使用模板化的ChangedEvent类吗?

看看这个例子: How to store templated objects of different type in container?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-