更新Lambda的参考数据

时间:2014-07-07 17:39:51

标签: c++ c++11 memory-management lambda malloc

所以我一直致力于内存管理,并且在移动包含包含捕获数据的lambda的函数对象的对象时遇到了一个特殊问题。假设以下示例:

typedef std::function < void( int ) > funcType;

class Something
{
private:
    int _myNum = 0;

public:
    funcType GetSetIt( )
    {
        return [&] ( int a )
        {
            _myNum = a;
        };
    }

    void SeeIt( )
    {
        std::cout << _myNum << std::endl;
    }

    int GetIt( )
    {
        return _myNum;
    }
};

以下操作:

auto destination = ( Something* ) malloc( sizeof( Something ) );
auto alt = ( funcType* ) malloc( sizeof( funcType ) );
auto size = sizeof( funcType );

auto s = new Something( );

auto setIt = s->GetSetIt( );
setIt( 10 );
s->SeeIt( );
auto a = s->GetIt( );

memcpy( destination, s, sizeof( Something ) );
memset(s, 0, sizeof( Something ) );

memcpy( alt, &setIt, sizeof( funcType ) );
memset( &setIt, 0, sizeof( funcType ) ); // point 1

(*alt)( 15 );
destination->SeeIt( );
auto b = destination->GetIt( );

快速解释:

创建一个新的Something并调用其所有成员以确保其正常运行。然后将其移动到新位置并删除/清除以前存在的位置。同时将函数对象移动到新位置并在之后清理。然后,使用指针指向新位置,调用函数对象和对象上的方法。

第一个问题是一切都在顺利进行,直到我memset函数对象的原始位置。如果你注释掉那一行(用// point 1标注),你会注意到它没有崩溃。

这对我来说有点奇怪,但我并不完全理解功能对象是如何在内存中布局的,并且希望在该区域有一些光线。我假设如果我将整个对象块复制到另一个区域并清除旧空间(不删除它,因为它在堆栈上),它将及其所有引用都将被保留。

第二个问题,假设您已注释掉memset行,则“预期结果”与“预期结果”不同。我希望调用alt会将_myNum上的s设置为15,确实如此。但我想更新alt指向Something的指针(我通常将其称为this指针)指向destination。我怎样才能做到这一点?它可以在编译器之间可靠地完成吗?我一直担心,虽然我可以想象它能够找到存储的位置并更新价值,但解决方案并不可靠,因为lambda可以在编译器之间以各种方式实现,并且可能存在一些“魔力”。

非常感谢您对这些问题的任何帮助或见解。如果我不清楚发生了什么,请评论,我会在需要的地方提供更多详细信息。提前谢谢!

2 个答案:

答案 0 :(得分:3)

function不可轻易复制(3.9p9,9p6),因此您无法使用memcpy复制它。使用is_trivially_copyable特征来检测类型是否可以轻易复制。

如果要将非平凡可复制类型的对象从一个位置“移动”到另一个位置,请使用placement new及其移动构造函数,并在之前的位置执行析构函数调用:

new (*buf) T(std::move(obj));
obj.~T();

答案 1 :(得分:0)

您应该使用placement new并确保从复制的对象中取出setter:

#include <functional>
#include <iostream>

// ...

int main() {
    char source[sizeof(Something)];
    char source_setter[sizeof(funcType)];
    Something* src = new (source) Something;
    // Get the setter from the source object.
    funcType* src_setter = new (source_setter) funcType(src->GetSetIt());
    (*src_setter)(0);

    char destination[sizeof(Something)];
    char destination_setter[sizeof(funcType)];
    Something* dst = new (destination) Something(*src);
    // Get the setter from the destination object.
    funcType* dst_setter = new (destination_setter) funcType(dst->GetSetIt());
    (*dst_setter)(1);

    src->SeeIt();
    dst->SeeIt();

    src_setter->~funcType();
    src->~Something();
    dst_setter->~funcType();
    dst->~Something();
}