0

功能上相同的是使用回调来自定义类行为,以及使用虚函数和继承。但我发现在我自己的工作中使用函数对象稍微灵活一些。

使用继承和许多类自定义行为

class Animal
{
    virtual void onTouch() = 0 ;
} ;

class Dog : public Animal
{
    void onTouch() 
    {
        // all dogs behave this way.
        // to change I'd need to subclass, or add parameters.
        puts( "Woof" ) ;
    }
} ;

使用函数对象按实例自定义类行为

struct Animal
{
    function<void ()> onTouch ;
} ;


Animal dog1,dog2 ;
dog1.onTouch = dog2.onTouch = [](){
    puts( "Woof" ) ;
} ;

Animal dog3 ;
dog3.onTouch = [](){
    // slightly modified behavior from dog1 and dog2, without
    // having to subclass, add members, or pass extra parameters
    puts( "Arr.. Woof" ) ;
} ;

有没有人知道我在这里所做的事情或任何使用它的理由

4

2 回答 2

3

说这比虚函数“稍微灵活一点”就像说goto比 if/else 稍微灵活一点。使用您的替代方案,您可以:

  • 动态更改对象的行为(只需分配一个新对象)
  • 在具有多个这些的类中混合和匹配行为,相反,从不强制两个相关的实现总是在这样的类中一起使用。
  • 本质上使类的每个实例化都成为子类。

但是,函数对象不能访问“他们的”对象的受保护成员,这可能需要您打破对宿主类的封装,以便函数对象可以做他们需要做的事情。

总而言之,这似乎是将对象与应该由该对象启动的外部行为(例如,按钮单击处理程序)相关联的好方法,但是使用它来扩展类的固有行为似乎会产生更多问题它解决了。

使用这种范例的最常见示例是回调函数或事件处理程序,因为该类关注绘制按钮和处理鼠标事件的机制(对于按钮类),或管理网络协议(对于网络套接字类)。在这些情况下,将事件处理程序设为虚拟并在子类中实现它并不是好的面向对象设计,因为子类在概念上并不是其父类的新“类型”,就像您不应该对按钮进行子类化一样改变它的高度和宽度。

于 2012-11-18T04:14:24.427 回答
0

这种类型的对象定制对于需要具有不同行为的对象类型是绝对必要的,但在基本结构上的差异不足以保证子类化

这最常出现在具有OnClick类型回调函数的 Window 元素类型中。如果我们Button每次想要不同的onClick行为时都对基本类进行子类化,那么您将有大约 70 个子类的Button行为方式完全相同,除了在onclick触发回调时发生的情况(class OkButtonForMainWindowDialog, class CancelButtonForMainWindowDialog..)。

错误的继承示例

class Button
{
    virtual void onClick( float x, float y ) = 0 ;
} ;

class OkButtonForMainWindowDialog : public Button
{
    virtual void onClick( float x, float y )
    {
        // Ok, so close this dialog box.. and commit the changes..
    }
} ;

相反,我们定义了基类Button,并允许附加一个指定onClick行为的函数对象。

基于函子的示例

class Button
{
    function< void ( float x, float y ) > onClick ;
} ;

Button okButton ;
okButton.onClick = []( float x, float y ){
    // do whatever's necessary..
} ;

使用这样的回调对于防止子类像这样成倍增加是绝对必要的。

于 2013-05-17T20:59:51.913 回答