函数指针,Functor还是Lambda

Function pointer, Functor or Lambda?

本文关键字:还是 Lambda Functor 指针 函数      更新时间:2023-10-16

我对C++还比较陌生,在Obj-C工作了多年,想知道如何将Obj-C中的闭包块添加到C++类中。下面是我想做的一些伪代码:

class Slider
{
public:
    void onMouseDown()
    {
        if(rightClick or ctlKeyDown)
        {
            if(myFunctionPointer != nil)
            {
                // show popup menu
                myFunctionPointer(this);
            }
        }
    }
    FunctionPointer myFunctionPointer = nil;
};

class Editor
{
public:
    void showPopupMenu(Slider *s)
    {
        // build the popupMenu with information based on the slider
    }
    void init()
    {
        // create a slider and connect the popupMenu function to it
        mySlider = new Slider;
        mySlider->functionPointer = showPopupMenu();
    }
    Slider *mySlider;
};

正如你所看到的,我正试图让Slider类在不了解它的情况下调用一个函数

这应该没那么难,但我有兴趣以最好/合适的方式做。Lambda和函子看起来令人难以置信地困惑。也许我在找别的东西。但是什么?

当将函数视为对象时,您的基本选项是:函数指针、函数/lambda、std::函数。我假设您可以找到它们的语法,并将重点放在它们的差异上。

当不需要闭包时,应该使用函数指针。当您要调用的过程是无状态的或具有全局状态,并且您在作用域中具有所有参数时,这将适用。

当您需要创建闭包时,应该使用函子。由于函子是对象,因此可以维护内部状态并在闭包内部传递参数。

lambda本质上是一个函子,没有显式的类型名。如果lambda被实现为函子,那么它的捕获列表就是它的成员。请注意,可以为函子重载operator(),但不能为lambda重载。

函子/lambdas的问题是,它们的每个定义都有不同的类型,并且可能不适合函数签名/类成员类型。std::function通过接受functor/lambda/函数指针并将它们转换为统一类型的std::函数来解决问题。不过,您为这种灵活性以性能的形式付出了(通常是很小的(代价。

Lambdas和函子是最高级的C++主题之一。你最好先从一些基础知识开始,并对C++类的工作原理有一个坚实的理解。

但是,既然你问了,C++的等价物应该是这样的:

class Slider
{
public:
    void onMouseDown()
    {
        if(rightClick or ctlKeyDown)
        {
            if(myFunctionPointer)
            {
                // show popup menu
                myFunctionPointer(this);
            }
        }
    }
    std::function<void (Slider *)> myFunctionPointer=nullptr;
};

class Editor
{
public:
    void showPopupMenu(Slider *s)
    {
        // build the popupMenu with information based on the slider
    }
    void init()
    {
        // create a slider and connect the popupMenu function to it
        mySlider = new Slider;

        mySlider->functionPointer = [this](Slider *)
             {
                   showPopupMenu();
             };
    }
    Slider *mySlider;
};

正如我所说,我认为你最好把精力集中在基本面上,首先,在跳进这些鲨鱼出没的水域之前。

只是为了添加一些额外的颜色:这将进行编译(唯一缺少的是rightClickctlKeyDown的定义(,但它可能是正确的,也可能不是正确的,这取决于所涉及对象的范围和生存期。让lambda捕获std::shared_ptr而不是this可能是必要的,也可能不是必要的,这取决于该应用程序中的对象是如何实例化的。在处理此类闭包和回调之前,了解C++对象是如何工作的是一个必要的先决条件

有不同的方法可以实现您想要的目标。这里有一种避免函数指针的方法。

我没有纠正其他一些明显的错误,比如由于调用new而从未删除对象而导致的内存泄漏。这种情况下的最佳实践是使用std::unique_ptr

class Slider
{
public:
    Slider(Editor& e)
  : _e(e)
  { }
    void onMouseDown()
    {
        if(rightClick or ctlKeyDown)
        {
            _e.showPopupMenu(this);
        }
    }
    Editor& _e;
};

class Editor
{
public:
    void showPopupMenu(Slider *s)
    {
        // build the popupMenu with information based on the slider
    }
    void init()
    {
        // create a slider and connect the popupMenu function to it
        mySlider = new Slider(*this);
    }
    Slider* mySlider;
};

下面是另一个直接在构造函数中移动函子的解决方案,但要使用模板。

template <typename Handler>
class Slider
{
public:
        Slider(Handler&& h)
        : _h(std::move(h))
        { }
    void onMouseDown()
    {
        if(rightClick or ctlKeyDown)
        {
           // show popup menu
           _h(this);
        }
    }
        Handler _h;
};

class Editor
{
public:
    void showPopupMenu(Slider *s)
    {
        // build the popupMenu with information based on the slider
    }
    void init()
    {
        // create a slider and connect the popupMenu function to it
        mySlider = new Slider([this](Slider* s){ showPopupMenu(s); });
    }
    Slider *mySlider;
};

您也可以使用std::function,如另一个答案

所示