函数指针,Functor还是Lambda
Function pointer, Functor or Lambda?
我对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;
};
正如我所说,我认为你最好把精力集中在基本面上,首先,在跳进这些鲨鱼出没的水域之前。
只是为了添加一些额外的颜色:这将进行编译(唯一缺少的是rightClick
或ctlKeyDown
的定义(,但它可能是正确的,也可能不是正确的,这取决于所涉及对象的范围和生存期。让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
,如另一个答案
- enum是C++中的宏变量还是整数变量
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- 在命名空间中定义函数还是限定函数
- 可组合的lambda/std::函数与std::可选
- 架构决策:返回std::future还是提供回调
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 异常属于C++中的线程还是进程
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 在决定是通过参考还是通过价值时,尺寸真的是一个问题吗
- 如何在C++中确定文本文件中的元素是字符还是数字
- 未评估上下文中的默认模板参数和 lambda:错误还是功能?
- 标准是否指定按值捕获的 lambda 是捕获父对象,还是仅捕获正在访问的成员
- C2665在Visual 2010中带有lambda函数和enum,这是一个错误还是正常
- 使用 Lambda 时,通过捕获子句按值传递还是通过作为参数传递来传递性能更高
- ::运算符 new 是不允许在 lambda 表达式中还是编译器错误?(更新!
- sizeof可以应用于未捕获的变量的lambda内部吗?还是这是一个编译器错误?
- 使用lambda填充向量以确定索引是偶数还是奇数
- 函数指针,Functor还是Lambda