草庐IT

C++函数不接受具体实现

coder 2024-02-04 原文

我正在尝试实现类型安全的事件总线。我坚持使用 EventBus::subscribe 函数,因为它不接受我的具体事件处理程序。在较早的版本中,我只将 AbstractEventHandler 实现为抽象类,而不是模板。我对该实现没有任何问题。这就是为什么我认为实际问题出在抽象模板上。

下面的代码是我的实现的精简版本。第一个 block 由事件总线的“骨架”及其所需的类组成,而第二个 block 显示事件、事件处理程序和主体的实际实现。

enum 包含所有可用的不同事件。抽象事件是所有具体事件派生的基础。事件处理程序是一个抽象模板,以事件作为模板类以确保类型安全。事件总线负责将所有已发布的事件分发到其各自的处理程序。

enum EVENT_TYPE 
{
    ON_EVENT_1,
    ON_EVENT_2
};

class AbstractEvent 
{
public:
    AbstractEvent() {};
    virtual ~AbstractEvent() {};

    virtual EVENT_TYPE type() = 0;
};

template<class T>
class AbstractEventHandler 
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
};

class EventBus 
{
public:
    EventBus() {};
    virtual ~EventBus() {};

    void subscribe(EVENT_TYPE type, 
                    AbstractEventHandler<AbstractEvent> *eventHandler) {
        // Add handler to vector for further use
    }

    void publish(AbstractEvent *event) {
        // send event to each handler in respective vector
    }
};

下面是我的具体事件和事件处理程序以及 main()

class ConcreteEvent : public AbstractEvent 
{
public:
    ConcreteEvent() {};
    virtual ~ConcreteEvent() {};

    EVENT_TYPE type() {
        return ON_EVENT_1;
    };
};

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{
public:
    ConcreteEventHandler() {}
    virtual ~ConcreteEventHandler() {};

    void on_event(ConcreteEvent *event) {
        // Do something
    };
};

int main() 
{
    EventBus *eventBus = new EventBus();

    ConcreteEventHandler handler = ConcreteEventHandler();

    // This failes!
    eventBus->subscribe(ON_EVENT_1, &handler);
}

编译器返回一个错误,指出没有匹配的函数来调用

EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

而且唯一的候选人是

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

如何实现我的 EventBus::subscribe 方法来接受抽象类的具体实现?

更新:解决方案

我已将 EventBus::subscribe 的方法描述更改为以下内容,现在它运行良好:

template<typename T>
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) {

}

谢谢 Rohan 的提示!他们帮助我找到了这个解决方案。

最佳答案

原因是因为,ConcreteEventHandlerAbstractEventHandler<ConcreteEvent> 的子类而不是 AbstractEventHandler<AbstractEvent> .

这可能看起来令人惊讶,但是 AbstractEventHandler<ConcreteEvent>不能是 AbstractEventHandler<AbstractEvent> 的子类尽管 ConcreteEventAbstractEvent 的子类.

原因是,使用模板,随心所欲地制作模板并不能保证类型安全。让我们看一个例子。让我们回顾一下基类的标准范例 Animal和子类 CatDog .假设我们有一个动物列表:

std::list<Animals>* animals;

和猫的列表:

std::list<Cat> cats;

以下不是有效的转换:

animals = &cats;

原因是,如果我要这样做,

animals->add(new Dog("Ben"));

我实际上会添加一个 DogCat 的列表秒。 cats.last()这里实际上会返回一个 Dog .因此,在这种情况下,您实际上是在添加一个 Dog。到 Cat 的列表秒。我看过足够多的 Looney Tunes 剧集,知道这不是一个好主意:

cats.last().meow();

以上肯定不是真的,因为我们都知道一个Dog只能bowbow() .

编辑

为了回答您的问题,我建议您这样做;让ConcreteEventHandler继承自 AbstractEventHandler<AbstractEvent> , 在代码中,无论你在哪里使用 ConcreteEvent , 使用 dynamic_case类型转换AbstractEventConcreteEvent .这将使用运行时自省(introspection),这可能会稍微影响性能(我也看到很多人反对使用动态转换),但您将能够成功执行数据类型的有效向上转换。

关于C++函数不接受具体实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9972968/

有关C++函数不接受具体实现的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  3. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  6. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

随机推荐