在C ++中使用回调是否会增加耦合?

时间:2009-11-13 08:10:12

标签: c++ oop callback

  

Q1。为什么使用回调函数?

     

Q2。回调是邪恶的吗?有趣的是那些   谁知道,对别人来说是一场噩梦。

     

Q3。回调的替代方法吗?

8 个答案:

答案 0 :(得分:26)

回调减少了耦合 - 被调用方传递了一些指针,它不知道它背后是什么。回调是如此幸运的解决方案,它们非常普遍。

例如,请查看sqlite3_exec()。您给它一个查询和可选的回调。它执行查询并在检索时调用每一行的回调。现在,SQLite的业务是快速执行查询并且资源消耗低,只有您的业务才能按照您的喜好处理检索到的结果。你可以将它们添加到一个容器中并稍后处理它们,或者你可以一个一个地立即处理它们,或者你可以将它们传真到某个地方并期望另一方传回它们 - SQLite并不关心,它是完全抽象的,可以只是做好自己的工作。

答案 1 :(得分:4)

无论是否使用“C ++中的回调增加耦合”,我建议使用事件处理程序样式,尤其是类似于事件的事件。例如,具体的设计模式而不是回调概念如下:

class MyClass
{
public:
    virtual bool OnClick(...) = 0;
    virtual bool OnKey(...) = 0;
    virtual bool OnTimer(...) = 0;
    virtual bool OnSorting(...) = 0
    ...
};

您可能仍然认为上述函数是回调函数,但在将它们视为已知的设计模式时,您不会感到困惑,因为您正在进行OO并编写C ++。

Effo UPD @ 2009nov13 - 典型案例:框架,事件系统或并发编程模型等。以下示例应该有用。

框架控制整体流程,因为好莱坞原则声明“不要打电话给我们,我们会打电话给你。” (这就是“回调”意味着什么),而对于普通函数或lib,调用者控制流。

一个着名的C框架是Linux内核,Linux驱动程序编写器知道他/她实现了“struct file_operations”,其中“read()”表示OnRead(),“write()”表示OnWrite( )等。

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ...
};

但最简单的框架示例应该是:

    The Framework           |     A developer to do
----------------------------+--------------------------------------
                            |    class MyClass : public Actor
                            |    {     
                            |    public:
pApplication->Init(...);    |        virtual bool OnInit(...) {}
pApplication->Run(...);     |        virtual int OnRun(...) {}  
pApplication->Quit(...);    |        virtual void OnQuit(...) {}
                            |        ...
                            |    };

和pApplication-> Init()将调用pActor-> OnInit,pApplication-> Run()调用pActor-> OnRun(),依此类推。大多数Windows GUI开发人员都经历过实现OnMouseClick()或OnButtonPress()等。

我同意这个线程的其他答案,他们相应地基于视点给出了正确的解释,例如分层方法中的处理程序,通用回调或异步操作等等。你有什么想法可以适合你。

答案 2 :(得分:3)

Q3。回调的替代方法吗?

我更喜欢函数形式的回调。例如这种事情:

class WidgetContainerOrProcessorOfSomeSort
{
public:
  struct IWidgetFunctor
  {
    virtual bool operator()( const Widget& thisWidget )=0;
  };
  ....
  bool ProcessWidgets( IWidgetFunctor& callback )
  {
     .....
     bool continueIteration = callback( widget[ idxWidget ] );
     if ( !continueIteration ) return false;
     .....
  }
};

struct ShowWidgets : public WidgetContainerOrProcessorOfSomeSort::IWidgetFunctor
{
  virtual bool operator(){ const Widget& thisWidget }
  {
     thisWidget.print();
     return true;
  }
};

WidgetContainterOrProcessorOfSomeSort wc;
wc.ProcessWidgets( ShowWidgets() );

对于简单的样本,它似乎总是有点冗长,但在现实世界中,我发现它比想要记住如何构造复杂函数指针声明要容易得多: - )

答案 3 :(得分:1)

  1. 回调会减少耦合,因为它们允许您编写调用可以更改的函数的代码
  2. 回调不是邪恶的,只是如果你使用原始函数指针,事情就会很快变得混乱。
  3. 本身没有回调的替代方法,但有其他方法可以使用原始函数指针。
  4. Boost.Function早先发布了指向Boost的帖子。如果您正在寻找更通用的回调解决方案,例如附加到同一回调的多个函数或类似的回调,请考虑使用Boost.Signals。这个名字来自信号和插槽,这是现在有些人引用回调的方式,特别是对于GUI。

答案 4 :(得分:0)

  1. 回调用于调用异步操作,即调用者按照自己的方式在不同的线程中运行的代码。您需要一些机制来了解异步操作何时完成。

  2. 他们为什么会变邪恶?与任何其他编程资源一样,它们在明智地使用时非常有用。实际上,Windows API和.NET框架广泛使用回调。

  3. 不了解C ++,但在.NET世界中,同步化对象和事件是替代方案。

答案 5 :(得分:0)

Q1。如果您使用分层方法,其中较高级别调用较低级别并通过回调从较低级别获得反馈,则需要回调 Q2。采取一些预防措施时,它们并不比例如例外。在某些方面,它们是相似的。 Q3。更多耦合:较低级别的人知道更高。

说明:
- 简单方法(每个回调1个回调处理程序):通过接口注册CallbackHandler对象
- 使用信号(QT,boost,...),并确保每次回调使用唯一信号以增强可追溯性

编辑:示例:

用户调用ProtocolHandler发送消息,ProtocolHandler调用用户发送回复:相互依赖。

分层:用户级别较高,ProtocolHandler级别较低。在启动时,它会为回复注册一个回调,并调用ProtocolHandler来发送消息。 ProtocolHandler使用回调来发送回复:只有用户依赖于ProtocolHandler。

答案 6 :(得分:0)

如果我们在C ++上下文中,请考虑使用(例如generalized callbacks)。

基本的行为思想是回调(类方法)可以是任何名称,并且不需要从回调执行程序知道的某个类派生回调。

回调的唯一限制是输入参数和返回值。所以这会将夫妻减少到零......:)

UDP:

@EffoStaff的答案Effo是一个回调应该是特定类(派生)并具有修正名称的示例。在一般化回调的背景下,所有这些“限制”都不存在。

答案 7 :(得分:0)

在极少数情况下,使用Win32样式的回调会注意“模糊的重入”问题(如果通过回调你只是意味着非常基本地将函数作为arg传递给另一个并发问题是不可想象的函数)。登记/>  Joe Duffy的“Windows上的并发编程”的优秀索引列出了“reentrancy”下的5个主题,这些主题通常是“围绕Rone-Hood的谷仓回归”概念的特殊化 - 换言之,来自最高评级-answer“被调用的一方通过一些指针,它不知道它背后是什么。”,“不知道”有时可以引导 - 罗宾汉的谷仓。
我刚刚说的任何内容都不是特定于回调,但如果被调用方在Duffy的一个场景中遇到困难,则可能发生“模糊的重入”。换句话说,隐含在“回调”的概念中,你似乎会在同一个线程中回调,这是如何发生的,它是如何同步的。
如果你谷歌在Duffy的书名上并在你的搜索中添加Callback这个词,那么你会得到超过10页的点击。