首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在闭包中,指针或引用类型是如何在内存中存储或用现代函数语言处理的?

在闭包中,指针或引用类型是如何在内存中存储或用现代函数语言处理的?
EN

Stack Overflow用户
提问于 2018-01-08 08:38:48
回答 1查看 218关注 0票数 2

为了教育目的我正在写一本特兰斯佩勒。我的播音器从我的语言到C语言。

我现在正在编写闭包语法分析器和代码生成组件。

我看到人们说,C++中的闭包实际上被转换为未命名的结构类型,其中捕获的值作为变量。

Here is the reference

这个代码

代码语言:javascript
复制
int c = 10;
auto closure = [=] () -> void {
    std::cout << c << std::endl;
};

转化成这样的东西--基本上是在引擎盖下面,所以他们说。

代码语言:javascript
复制
struct UNNAMED_TYPE_0 {
    int c;

    void operator() () const {
        std::cout << c << std::endl;
    }
};
// assume the closure is initialized and variables are assigned

如果有人想在闭包执行时改变这个int c,他/她必须将这个变量作为ref [&c] () -> void { /* mutation comes here */}传递。但问题是,如果我们在函数中声明int c并在函数中创建闭包,则如下所示

代码语言:javascript
复制
function<void()> aFunction() {
    int c = 10;
    auto closure = [&c] () -> void { c = 100; }
    return closure;
}

aFunction() ();

int c被捕获,但一旦aFunction堆栈被销毁,int c也被销毁。这意味着,如果我们尝试在已释放的地址上写入,我们可能希望运行segmentation fault(core dumped)指针错误。

Java中,

代码语言:javascript
复制
// suppose this callback interface exists
public interface VoidCallback {
    public void method();
} 


public void aMethod() {
    int c = 10;
    VoidCallback callback = () -> c = 10; /* this gives an error */
    // local variables referenced from a lambda expression must be final or effectively final
}

Java处理这样的闭包,并确保闭包捕获没有任何变化(比如隐式捕获)。这意味着Java传递闭包捕获的是副本,而不是引用。对于引用或类类型,只有对象指针作为副本传递。尽管指针引用不发生变异,但可以对指针指向的对象内的内容进行变异。这基本上和以前的一样。

Objective-C,中

代码语言:javascript
复制
__block int c = 0;
// they say, this `int c` is allocated in heap instead of stack
// so that it exists until the closure is finished executing.
void (^ closure) (void) = ^void() {
    c = 10; // this is valid
    // this actually changed the `int c`'s value
}; 

Swift

代码语言:javascript
复制
var a : Int = 10;
var closure = { [] () -> Void in
    a = 10; // this is valid by default
    // unless `var a` is declared as `let a`
};

因此,这意味着,Objective和Swift将基本捕获列表作为指针分配.这样他们就可以变异了。

P.S:请注意,Swift闭包捕获列表只适用于类或引用类型,但我的意思是在这里隐式捕获基元类型。

,我是

代码语言:javascript
复制
__block int c = 0;
// they say, this `int c` is allocated in heap instead of stack
// so that it exists until the closure is finished executing.
void (^ closure) (void) = ^void() {
    c = 10; // this is valid
    // this actually changed the `int c`'s value
}; 

与(基本上)对此相同

代码语言:javascript
复制
int * c = malloc(sizeof(int));
*c = 0;
void (^ closure) (void) = ^void() {
    *c = 10;
    if (c) {
        free(c);
        c = NULL;
    }
}; 

我认为,一旦完成闭包,释放指针变量就太糟糕了。

如果有大量的闭包指向变量并在执行时发生变异,怎么办?

如果这些闭包在不同线程之间传递或执行怎么办?

我想出了一个使用参考计数技术的解决方案。

当创建一个变异变量的闭包时,该变量将被保留。

当改变变量的闭包被破坏时,变量将被释放。

当没有闭包时,变量将被真正释放。为了确保线程安全,我将锁定和解锁计数器变量地址,因为闭包操作引用计数技术。

如果有别的技术,请指导我。

任何语言的任何解释都是非常感激的。

目前,我对汇编语言一窍不通。

对于主持人来说,由于这个问题是一种研究,我恳请你们不要过于宽泛。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-08 09:24:58

以下给我留下的印象是:“我正在为教育目的写一个特兰斯佩勒,我的播音器从我的语言转到C语言。”现在,这意味着您的语言规范定义了它应该如何操作!我们不能告诉你你的语言应该如何运作。

现在,您已经找到了许多选项:

  • C++不对局部变量执行任何特殊操作。如果您保留了对它的引用,并在它超出范围时使用它,则会带来厄运。这就是C++的精神,不要把任何开销放在你身上,而是允许你在不注意的情况下朝自己的脚开枪。
  • Java只需检查代码,并告诉您是否试图执行它认为不一定有效的任何操作,否则会给您一个错误。它不允许你开枪打自己的脚,即使你非常想要它。
  • 其他语言似乎将范围有限的局部变量转换为基于堆的变量。我不确定它们的对象模型,但是在Python中,您根本没有任何类似于C++局部变量或with原语类型的东西,就像您没有确定性析构函数调用一样(您在那里使用with获得了类似的东西,只是为了完整性),所以这没有任何区别。这些语言给您增加了开销,以保证您没有任何悬空的引用(即使您并不真正需要它)。

现在,第一件事是决定哪一个最适合您的语言的对象模型。只有到那时,问题才会出现:如何最好地实现它。关于实现,有许多不同的方法。使用引用计数器是一种(虽然是用无锁原子操作实现的),使用链接列表是另一种,或者使用垃圾收集器。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48146679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档