函数指针、Functor 还是 Lambda?
Function pointer, Functor or Lambda?
我是 C++ 的新手,在 Obj-C 上花了很多年,想知道如何将 Obj-C 中的闭包块添加到 C++ class。这是我想做的一些伪代码:
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 class 在不知道任何相关信息的情况下调用一个函数。
这不应该那么困难,但我有兴趣以 best/proper 的方式进行。 Lambdas 和仿函数看起来令人难以置信的混乱。也许我正在寻找别的东西。但是什么?
有多种方法可以实现您的目标。
这是一种避免函数指针的方法。
我没有更正其他一些明显的错误,例如因调用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
,如另一个答案所示
Lambda 和仿函数是最高级的 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;
};
正如我所说,我认为在进入这些鲨鱼出没的水域之前,您最好先将精力集中在掌握基本知识上。
并且只是为了添加一些额外的颜色:这将编译(唯一缺少的是 rightClick
或 ctlKeyDown
的定义),但它可能正确也可能不正确,具体取决于所涉及对象的范围和生命周期。让 lambda 捕获 std::shared_ptr
而不是 this
可能有必要,也可能没有必要,具体取决于此应用程序中对象的实例化方式。在处理此类闭包和回调之前,了解 C++ 对象的工作原理是必要的先决条件。
当谈到将函数作为对象处理时,您的基本选择是:函数指针、functor/lambda、std::function。我假设您可以找出它们的语法并将重点放在它们的区别上。
当不需要闭包时,应该使用函数指针。这适用于您要调用的过程是无状态的或具有全局状态,并且所有参数都在范围内的情况。
当您需要创建闭包时,应该使用仿函数。由于仿函数是对象,您可以维护内部状态并在闭包内传递参数。
lambda 本质上是一个函子,没有明确的类型名。如果 lambda 是作为仿函数实现的,则它的捕获列表就是它的成员。请注意,您可以为仿函数重载 operator()
但不能为 lambda 重载。
functor/lambdas 的问题在于它们的每个定义都有不同的类型,并且可能不适用于函数 signature/class 成员类型。 std::function 通过能够接受 functor/lambda/函数指针并将它们转换为 std::function 的统一类型来解决问题。不过,您以性能的形式为这种灵活性付出了(通常很小的)代价。
我是 C++ 的新手,在 Obj-C 上花了很多年,想知道如何将 Obj-C 中的闭包块添加到 C++ class。这是我想做的一些伪代码:
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 class 在不知道任何相关信息的情况下调用一个函数。
这不应该那么困难,但我有兴趣以 best/proper 的方式进行。 Lambdas 和仿函数看起来令人难以置信的混乱。也许我正在寻找别的东西。但是什么?
有多种方法可以实现您的目标。 这是一种避免函数指针的方法。
我没有更正其他一些明显的错误,例如因调用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
,如另一个答案所示
Lambda 和仿函数是最高级的 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;
};
正如我所说,我认为在进入这些鲨鱼出没的水域之前,您最好先将精力集中在掌握基本知识上。
并且只是为了添加一些额外的颜色:这将编译(唯一缺少的是 rightClick
或 ctlKeyDown
的定义),但它可能正确也可能不正确,具体取决于所涉及对象的范围和生命周期。让 lambda 捕获 std::shared_ptr
而不是 this
可能有必要,也可能没有必要,具体取决于此应用程序中对象的实例化方式。在处理此类闭包和回调之前,了解 C++ 对象的工作原理是必要的先决条件。
当谈到将函数作为对象处理时,您的基本选择是:函数指针、functor/lambda、std::function。我假设您可以找出它们的语法并将重点放在它们的区别上。
当不需要闭包时,应该使用函数指针。这适用于您要调用的过程是无状态的或具有全局状态,并且所有参数都在范围内的情况。
当您需要创建闭包时,应该使用仿函数。由于仿函数是对象,您可以维护内部状态并在闭包内传递参数。
lambda 本质上是一个函子,没有明确的类型名。如果 lambda 是作为仿函数实现的,则它的捕获列表就是它的成员。请注意,您可以为仿函数重载 operator()
但不能为 lambda 重载。
functor/lambdas 的问题在于它们的每个定义都有不同的类型,并且可能不适用于函数 signature/class 成员类型。 std::function 通过能够接受 functor/lambda/函数指针并将它们转换为 std::function 的统一类型来解决问题。不过,您以性能的形式为这种灵活性付出了(通常很小的)代价。