如果我这样做: -
class Thing
{
...
void function (const std::string& message);
};
std::list<std::function<void()>> work;
和“Thing”的某些成员
work.push_back(std::bind(&Thing::function, this, "Hello"));
调用std :: bind还是使用std :: function&lt;&gt;使用new或其他方式导致任何动态内存分配?或者是在编译时分配的所有存储?如果标准没有说什么,那么在visual studio 2012中,我的程序只需要在那里构建,为了提高效率,我可能需要在我考虑使用这种机制的地方避免动态内存分配。 / p>
答案 0 :(得分:15)
标准没有指定,但一般来说很容易看出std::function
必须至少在某些情况下分配内存:
struct huge { char c[10000]; };
void foo(const huge &);
std::function<void()>{std::bind(foo, huge{})};
另一方面,它可以通过在function
对象的足迹内的预分配缓冲区内选择其函数对象来避免至少在某些情况下的分配;显然有一个权衡,因为这可能会使其他用途占用更多的堆栈内存。在function
对象中存储原始函数指针时,一个好的实现可以避免内存分配,也可能在mem_fn
中存储,但是它不太可能为{bind
执行此操作。 1}}。
例如,libstdc ++(g ++)内联(仿函数)对象指针,函数指针和(非虚拟)成员函数指针,以及适合相同覆盖范围的任何其他内容,例如:无状态仿函数(union _Nocopy_types
)。
如果可以,通过反转控制流来接受模板化仿函数对象而不是function
,可以避免任何额外的内存分配:
template<typename F>
void my_algorithm(const F &);
my_algorithm(std::bind(foo, huge{}));
答案 1 :(得分:1)
我只是针对g ++进行了一些研究。
关于std :: function和动态内存分配,有两个要点。
gccs libstd +++中std :: function的实现将在不进行动态内存分配的情况下存储大小/对齐要求小于或等于其必须存储的事物的大小/对齐要求的其他内容。
在没有动态内存分配的情况下,它必须存储的最大内容是指向成员函数的指针。在基于“ itanium c ++ ABI” * this is twice the size of a normal pointer的编译器上。因此,您可以在g ++中的std :: function中存储最多两个指针的任何内容,而无需触发动态内存分配。
据我所知std :: bind只是将内容串联到一个对象中,因此将任何内容绑定到成员函数将导致对象的大小至少为三个指针。将此对象分配给std :: function将导致动态内存分配。
更好的选择是使用lambda。这是静态地引用成员函数,从而为您提供了最多捕获两个指针的空间而无需触发动态内存分配。
为了演示,我根据您的情况松散地编写了一些测试代码。我摆脱了字符串和列表,并使用了const char *(以避免与std :: string相关的内存分配)和放置新的位置(此代码仅用于构建而不是运行),并将其馈入Godbolt。
#include <functional>
using namespace std;
class Thing
{
void foo();
void bar();
void function (const char * message);
};
char baz[1024];
void Thing::foo() {
new (baz) std::function<void()>(std::bind(&Thing::function, this, "Hello"));
}
void Thing::bar() {
const char * s = "Hello";
new (baz) std::function<void()>([this,s](){function(s);});
}
结果是。
Thing::foo():
mov r3, #0
push {r4, r5, r6, lr}
ldr r4, .L34
mov r6, r0
sub sp, sp, #16
mov r0, #16
str r3, [r4, #8]
bl operator new(unsigned int)
ldr r2, .L34+4
mov r1, #0
mov r3, r0
str r2, [sp]
mov r2, sp
ldr r5, .L34+8
ldr lr, .L34+12
ldr ip, .L34+16
str r1, [sp, #4]
str r6, [r0, #12]
str r0, [r4]
str r5, [r3, #8]
ldm r2, {r0, r1}
str lr, [r4, #12]
stm r3, {r0, r1}
str ip, [r4, #8]
add sp, sp, #16
pop {r4, r5, r6, pc}
ldr r3, [r4, #8]
cmp r3, #0
beq .L27
ldr r1, .L34
mov r2, #3
mov r0, r1
blx r3
.L27:
bl __cxa_end_cleanup
.L34:
.word .LANCHOR1
.word Thing::function(char const*)
.word .LC0
.word std::_Function_handler<void (), std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_invoke(std::_Any_data const&)
.word std::_Function_base::_Base_manager<std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
Thing::bar():
ldr r2, .L38
sub sp, sp, #8
stm sp, {r0, r2}
add r2, sp, #8
ldr r3, .L38+4
ldmdb r2, {r0, r1}
ldr ip, .L38+8
ldr r2, .L38+12
stm r3, {r0, r1}
str ip, [r3, #12]
str r2, [r3, #8]
add sp, sp, #8
bx lr
.L38:
.word .LC0
.word .LANCHOR1
.word std::_Function_handler<void (), Thing::bar()::{lambda()#1}>::_M_invoke(std::_Any_data const&)
.word std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}> const&, std::_Manager_operation)
我们可以清楚地看到在bind情况下有内存分配,而在lambda情况下没有。
* g ++和clang ++在许多不同的体系结构中都使用了这个名称。
答案 2 :(得分:0)
我不确定这一点。我想,正如ecatmur所说,它取决于该平台的std实现。对于类似的问题,我在代码项目中使用这个实现已经取得了很大的成功。它支持大量平台。记录很好,没有动态内存分配。
http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
应避免在游戏或模拟中运行时的通用动态内存分配。问题并不总是碎片化或大瓶颈(两者都是有效的理由可以避免),而是时间量通常是不确定的。更具域特定的内存分配策略,例如“汇集”或“成帧”在这里将是有利的。