函数指针,Functor或Lambda?

时间:2016-08-17 23:44:06

标签: c++ c++11 lambda function-pointers

对于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类在不知道任何相关内容的情况下调用函数。

这不应该那么困难,但我有兴趣以最佳/正确的方式做到这一点。 Lambdas和仿函数看起来令人难以置信。也许我正在寻找别的东西。但是什么?

3 个答案:

答案 0 :(得分:5)

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

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

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

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

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

答案 1 :(得分:2)

Lambda和functor是最高级的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 ++对象如何工作将是必要的先决条件。

答案 2 :(得分:-1)

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

我没有纠正其他一些明显的错误,例如调用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代替其他答案