在Perl中,为什么复制一个弱引用会创建一个普通、强引用?

10

Scalar::Util::weaken说:

注意:复制弱引用会创建一个普通的、强引用。

我不明白为什么Perl要这样处理。在我的应用程序中,我使用weaken来打破循环引用。有时我必须削弱本来如果Perl不这样操作已经是弱引用的引用。

3个回答

13

每当你将一个引用复制到一个新变量中时,引用计数都会增加。这适用于复制弱引用和强引用。

my $obj = {};    # 1 reference to {} stored in $obj

my $copy = $obj; # 2 references

weaken $obj;     # 1 reference

此时,如果$copy超出其作用域,引用计数将降至零,并释放内存。现在假设有以下代码:

my $newref = $obj;  # 2 references

undef $copy;        # 1 reference

如果 Perl 保留了 $newref 中的弱引用,那么当清除 $copy 时,哈希表会意外地被释放。这将打破一个期望:当你复制一个引用时,它至少会和原引用一样长久。

简而言之,如果弱引用在赋值后仍然存在,就必须在代码中添加大量的弱引用检查,并需要其他的方法来取消弱化变量,以避免不可避免的变量自杀问题。


10
我认为这是一个封装问题。如果第三方库在内部使用弱引用,那么我的代码不应该预先知道当我复制一个引用时,它可能会突然消失。在 Perl 中通常的期望是,只要存在,ref 将保持有效。当你调用 weaken 时,你基本上已经承诺在使用它之前将采取必要的步骤检查引用是否仍然有效。

作为第二个原因,使弱引用的强复制的接口相当直截了当。
my $new_ref = $old_ref; if (isweak($old_ref)) { weaken($new_ref); }

如果弱引用创建了一个弱引用,那么获取强引用的代码会更加复杂。

my $new_ref;
if (ref($old_ref) eq 'ARRAY') {
    $new_ref = \@{$old_ref};
}
elsif (ref($old_ref) eq 'HASH') {
    $new_ref = \%{$old_ref};
}
elsif (.....
如果您知道引用只能是一种类型,那么您可以省略if / elsif级联,并简单地执行deref-reref,但是仍然很难判断为什么要取消引用,然后再重新引用,下一个维护者将尝试“修复”您的代码。

3

我不确定为什么这是默认行为,但是以下解决方案基于来自Scalar::Util文档的代码:

$ref  = \$foo;
$weak = isweak($ref);               # false
weaken($ref);
$weak = isweak($ref);               # true

# copying a weak reference creates a new strong one
$copy = $ref;
$weak = isweak($copy);              # false

# the solution is simply to weaken the copy
$weaken($copy);
$weak = isweak($copy);              # true

如果你想要创建一个子程序,以弱引用作为参数并返回该引用的弱化副本,使用上述代码将很容易实现。

你确定子例程可以返回一个弱引用吗?在我看来,任何在子例程内创建的弱引用都会在子例程结束时超出作用域而被销毁。 - Dancrumb
返回一个弱引用是否会导致隐式复制(从而导致返回一个强引用)? - Cameron
2
@Dancrumb,@Cameron,你可以返回一个存储了弱引用的重载对象。 - ysth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接