C ++ 11 lambda实现和内存模型
我想了解一些关于如何正确思考C ++ 11闭包和std::function
的信息,以及它们如何实现以及如何处理内存。
尽pipe我不相信过早的优化,但是在编写新代码的时候,我确实有习惯认真考虑我的select对性能的影响。 我也做了相当数量的实时编程,例如在微控制器和audio系统上,要避免非确定性内存分配/释放暂停。
因此,我想更好地了解何时使用或不使用C ++ lambdaexpression式。
我目前的理解是,没有捕获闭包的lambda完全像Ccallback。 但是,当通过值或引用捕获环境时,会在堆栈上创build一个匿名对象。 当一个函数必须返回一个值闭包时,它会把它包装在std::function
。 在这种情况下closures内存会发生什么? 它是从堆栈复制到堆? 每当std::function
被释放时它是否被释放,也就是说,它是否像std::shared_ptr
一样被引用计数?
我想在一个实时系统中,我可以设置一个lambda函数链,将B作为延续parameter passing给A,以便创build一个处理pipe道A->B
在这种情况下,A和Bclosures将被分配一次。 虽然我不确定这些是否会被分配在堆栈或堆上。 但是总的来说,这在实时系统中似乎是安全的。 另一方面,如果B构造了一些返回的lambda函数C,那么C的内存将被重复地分配和释放,这对于实时使用是不可接受的。
在伪代码中,我认为将是一个实时安全的DSP环路。 我想执行处理块A然后B,其中A调用它的参数。 这两个函数都返回std::function
对象,所以f
将是一个std::function
对象,其环境存储在堆中:
auto f = A(B); // A returns a function which calls B // Memory for the function returned by A is on the heap? // Note that A and B may maintain a state // via mutable value-closure! for (t=0; t<1000; t++) { y = f(t) }
而且我认为在实时代码中可能不好使用:
for (t=0; t<1000; t++) { y = A(B)(t); }
还有一个我认为堆栈内存可能用于closures的地方:
freq = 220; A = 2; for (t=0; t<1000; t++) { y = [=](int t){ return sin(t*freq)*A; } }
在后一种情况下,闭包是在循环的每次迭代中构造的,但与前面的示例不同,它是便宜的,因为它就像函数调用一样,不会进行堆分配。 此外,我想知道一个编译器是否可以“解除”闭包并进行优化。
它是否正确? 谢谢。
我目前的理解是,没有捕获闭包的lambda完全像Ccallback。 但是,当通过值或引用捕获环境时,会在堆栈上创build一个匿名对象。
没有; 它始终是一个未知types的C ++对象,在堆栈上创build。 一个无捕获的lambda可以被转换成函数指针(尽pipe它是否适合C调用约定是依赖于实现的),但这并不意味着它是一个函数指针。
当一个函数必须返回一个值闭包时,它会把它包装在std :: function中。 在这种情况下closures内存会发生什么?
lambda在C ++ 11中没有什么特别之处。 这是一个像任何其他对象的对象。 lambdaexpression式产生一个临时的,可以用来初始化堆栈上的一个variables:
auto lamb = []() {return 5;};
lamb
是一个堆栈对象。 它有一个构造函数和析构函数。 它将遵循所有的C ++规则。 lamb
的types将包含捕获的值/参考; 它们将成为该对象的成员,就像任何其他types的其他对象成员一样。
你可以把它给一个std::function
:
auto func_lamb = std::function<int()>(lamb);
在这种情况下,它会得到一个lamb
价值的副本 。 如果lamb
有价值地捕获了任何东西,就会有两个这样的价值; 一个在lamb
,一个在func_lamb
。
当前范围结束时, func_lamb
清理栈variables的规则, func_lamb
将被销毁,接着是func_lamb
。
你可以很容易地在堆上分配一个:
auto func_lamb_ptr = new std::function<int()>(lamb);
std::function
内容的内存究竟在哪里取决于实现,但std::function
使用的types擦除通常至less需要一个内存分配。 这就是为什么std::function
的构造函数可以使用分配器。
每当std :: function被释放时它是否被释放,也就是说,它是否像std :: shared_ptr一样被引用计数?
std::function
存储其内容的副本 。 与几乎所有标准库C ++types一样, function
使用值语义 。 因此,它是可复制的; 当它被复制时,新的function
对象是完全分离的。 它也是可移动的,所以任何内部分配都可以适当地转移,而不需要更多的分配和复制。
因此不需要参考计数。
假设“内存分配”等同于“在实时代码中使用不好”,其他所有内容都是正确的。