我在这里缺少什么?
当像这样向 Net::Ping 传递参数时,初始化构造函数 Net::Ping->new($args) 后,$args 和 $args_copy 都将被设置为空的 hashref。
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use Net::Ping;
sub _ping {
my ($args) = @_;
my $p = Net::Ping->new($args);
$p->close();
}
my $args = { proto => 'udp' };
my $args_copy = $args;
print Dumper $args; # $VAR1 = { 'proto' => 'udp' }
print Dumper $args_copy; # $VAR1 = { 'proto' => 'udp' }
_ping($args);
print Dumper $args; # $VAR1 = {}
print Dumper $args_copy; # $VAR1 = {}
我在运行 Ubuntu 20.04.4 LTS 和 Perl v5.30.0 的 Strawberry Perl 和 WSL2 上看到相同的行为。
这很有趣,一个类(构造函数)删除调用者的数据。
所示代码将引用传递给
Net::Ping
构造函数,并且数据被清除,就在构造函数中(见下文)。
为了避免
$args
被清除,如果这是一个问题,请传递其副本
_ping( { %$args } );
这首先取消引用哈希,然后用它构造一个匿名哈希引用,†并传递它。所以
$args
是安全的。
构造函数new直接使用来自
@_
的数据(不制作本地副本),然后当它遍历键时,它也会删除它们,我认为是为了方便进一步处理。 (我承认这很可怕。)
由于引用被传递到
new
,调用代码中的数据可能会发生变化。‡
† 当复制包含复杂数据结构的哈希(或数组)时(当其值本身包含引用时),我们需要进行“深复制”。一种方法是使用 Storable
use Storable qw(dclone);
my $deep_copy = dclone $complex_data_structure;
这里的意思是
_ping( dclone $args );
。似乎
new
只能引用平面哈希(或标量),因此这是没有必要的。
当子程序直接使用它获得的引用时,它可以更改调用者中的数据
sub sub_takes_ref {
my ($ref_data) = @_;
for my $k (keys %$ref_data) {
$ref_data->{$k} = ...; # !!! data in caller changed !!!
}
}
...
my $data = { ... }; # a hashref
sub_takes_ref( $data );
但是,如果在子程序中创建参数的本地副本,则调用者的数据无法更改
use Storable qw(dclone); # for deep copy below
sub sub_takes_ref {
my ($ref_data) = @_;
my $local_copy_of_data = dclone $ref_data;
for my $k (keys %$local_copy_of_data) {
$local_copy_of_data->{$k} = ...; # data in caller safe
}
}
(请记住不要触摸
$ref_data
而是使用本地副本。)
当子程序要处理具有大量数据的数据结构时,这种在调用方中更改数据的方法当然很有用,因为这样就不必复制它们。但是,当这不是子程序的目的时,我们需要小心,或者只是制作本地副本以确保安全。