Loki 函子 - 内存问题

Posted

技术标签:

【中文标题】Loki 函子 - 内存问题【英文标题】:Loki functor - problem with memory 【发布时间】:2011-11-10 01:55:54 【问题描述】:

我在我的项目中使用 Loki::Functor 来构建一个简单的事件系统。该事件的处理函数采用一些参数。在这种情况下,它被称为PrintEventString。为了将它放入队列中,事件处理程序必须具有相同的原型——在我的例子中,void func(void)。所以CreateEvent 获取处理程序,从中创建函子并绑定参数,从而产生void f (void) 原型。一切正常(第一个示例,字符串存储在局部变量中),直到我在调用仿函数之前销毁数据源(第二个示例,临时创建的字符串)。代码如下:

#include <climits>
#include <string>
#include <iostream>
#include "Loki/Functor.h"

void PrintEventString(std::string str)

    std::cout << "Test: " << str << std::endl;


Loki::Functor<void> CreateEvent (std::string str)

    Loki::Functor<void, TYPELIST_1(std::string)> handler(PrintEventString);
    Loki::Functor<void> event (Loki::BindFirst(handler, str));
    return event;


int main (void)

    std::string hello("hello");

    Loki::Functor<void> eventTestLocal(CreateEvent(hello));
    eventTestLocal();

    Loki::Functor<void> eventTestTemp(CreateEvent("Hello world"));
    eventTestTemp();


    return 0;

这编译,执行,但第二个测试不起作用,valgrind 抛出一堆错误:

==30296== Memcheck,内存错误检测器 ==30296== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==30296== 使用 Valgrind-3.6.1 和 LibVEX;使用 -h 重新运行以获取版权信息 ==30296== 命令:./main ==30296== 测试:你好世界 ==30296== 大小为 4 的读取无效 ==30296== 在 0x40EB655: std::basic_string, std::allocator >::basic_string(std::string const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f2640 在大小为 24 的块中是 8 个字节 ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 4 的读取无效 ==30296== 在 0x40EAD96:std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f263c 在大小为 24 的块内有 4 个字节已释放 ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 4 的读取无效 ==30296== 在 0x40EADA5:std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f2638 是大小为 24 的块内的 0 个字节 free'd ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 4 的读取无效 ==30296== 在 0x40EADB3: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f2638 是大小为 24 的块内的 0 个字节 free'd ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 1 的读取无效 ==30296== 在 0x40294BA:memcpy(在 /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so 中) ==30296== by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f264e 在大小为 24 的块内是 22 个字节 ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 4 的读取无效 ==30296== 在 0x40294E8:memcpy(在 /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so 中) ==30296== by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f2648 在大小为 24 的块内是 16 个字节 ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so) ==30296== ==30296== 大小为 4 的读取无效 ==30296== 在 0x40EADF8:std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== 由 0x8048E7A: 主 (main.cpp:26) ==30296== 地址 0x42f2638 是大小为 24 的块内的 0 个字节 free'd ==30296== at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.16 中) ==30296== by 0x41A0232: (低于 main) (in /lib/libc-2.14.so)

我怀疑仿函数只引用传递的对象,然后将其销毁(作为临时创建的)并且问题开始了。但是我在这里做错了什么?我认为绑定是用于存储环境的一部分(正如 Andrei 在他的书中所描述的那样),以便可以破坏环境。

【问题讨论】:

您能否添加更多有关 Loki 版本以及如何编译源代码的信息?它不会在这里编译,因为我的 Loki 版本使用 LOKI_TYPELIST_1 而不是 TYPELIST_1 @evnu:我有本书提供的链接的正式版本。我能看到的唯一版本是上次库更新:2001 年 6 月 20 日 项目网站loki-lib.sourceforge.net在sourceforge.net/projects/loki-lib/files/Loki下列出了更多版本 【参考方案1】:

问题在于 Loki 的仿函数对象并没有制作字符​​串的真实副本,而是存储了对您希望绑定到函数的字符串对象的引用。这是因为如果被绑定的参数的类型不是指针、成员指针或算术类型(即可以执行算术运算的类型),loki 的仿函数对象会存储引用类型。因此,由于字符串是临时的,并且只存储了对临时的引用,一旦堆栈从函数调用中展开,对临时字符串的访问就会从活页夹对象的内部引用中丢失,并且您无法打印字符串。

一种可能的解决方案是创建您的函数,使其采用智能指针类型,以便您可以动态分配对象,并且对象的生命周期将超出当前范围,但避免围绕对象生命周期和使用普通或裸指针类型会发生的内存泄漏。

编辑:我试过了......似乎仍然无法正常工作,因为它再次存储了对智能指针类型的引用,这意味着当临时智能指针时指针被释放超出范围。所以是的,你要么必须改变一些关于 loki 的仿函数对象如何确定是存储引用还是值的定义,或者使用另一个版本的绑定参数到函数对象,比如新的 C++11 标准版本的 @ 987654321@和std::function

【讨论】:

谢谢,我以为是这样的。我喜欢 Loki::Functor,因为它使用简单。现在,当我需要自己处理深拷贝时,我可能会坚持使用 std::tr1。我测试了它(老实说,我什至不知道 std 有这样的东西)并且它更适合(因为它似乎在绑定时使用深拷贝)。

以上是关于Loki 函子 - 内存问题的主要内容,如果未能解决你的问题,请参考以下文章

Loki::Singleton、Loki::SmartPtr 和 std::vector 的奇怪内存问题

洛基(Loki)运行游戏出现问题需要关闭

如何在 Lua 中成功使用 Loki 的小对象分配器?

就内存使用而言,模板 + 仿函数/lambdas 不是最理想的吗?

进阶学习4:函数式编程FP——函子FunctorMayBe函子Either函子IO函子FolktalePointer函子Monad

游戏引擎架构入门