В Perl, почему копирование слабой ссылки создает нормальную, сильную ссылку? - PullRequest
10 голосов
/ 16 декабря 2010

Скаляр :: Утиль :: ослабить говорит:

ПРИМЕЧАНИЕ. При копировании слабой ссылки создается нормальная, сильная ссылка.

Я не могу понять, почему Perl так поступил. В своих приложениях я использую weaken чтобы разорвать циклы. Иногда мне приходится ослаблять ссылки, которые были бы уже слабыми, если бы Perl не действовал таким образом.

Ответы [ 3 ]

13 голосов
/ 17 декабря 2010

Каждый раз, когда вы копируете ссылку в новую переменную, счетчик ссылок увеличивается.Это верно при копировании слабой или сильной ссылки.

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 голосов
/ 16 декабря 2010

Я думаю, что это проблема инкапсуляции. Если сторонняя библиотека использует слабые ссылки внутри, мой код не должен заранее знать, что когда я делаю копию ссылки, она может внезапно исчезнуть у меня. Обычное ожидание в Perl состоит в том, что ссылка останется действительной до тех пор, пока она существует. Когда вы звоните по номеру 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 голосов
/ 16 декабря 2010

Я не уверен, почему это поведение по умолчанию, но вот решение, основанное на коде из документации 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

Если вы хотите сделатьподпрограмма, которая взяла слабую ссылку в качестве аргумента и вернула ослабленную копию этой ссылки, что было бы легко, используя код, показанный выше.

...