Loki仿函数 - 记忆问题

时间:2011-09-04 12:23:11

标签: c++ memory-management loki

我在项目中使用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, a memory error detector
==30296== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30296== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==30296== Command: ./main
==30296== 
Test: Hello world
==30296== Invalid read of size 4
==30296==    at 0x40EB655: std::basic_string, std::allocator >::basic_string(std::string const&) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2640 is 8 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EAD96: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f263c is 4 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADA5: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADB3: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 1
==30296==    at 0x40294BA: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f264e is 22 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40294E8: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2648 is 16 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADF8: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /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==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 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&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)

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

1 个答案:

答案 0 :(得分:1)

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

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

编辑:我试过了......似乎仍然没有工作,因为它再次存储对智能指针类型的引用,这意味着指针在临时智能指针时被释放超出范围。所以,是的,您要么必须更改loki的functor对象如何确定是否存储引用或值的定义,或者使用其他版本的绑定参数来处理函数对象,例如新的C ++ 11标准版本的{ {1}}和std::bind