为什么传递给 Net::Ping 构造函数的 Hashref,在 Net::Ping->new($args) 之后设置为空 hashref?

问题描述 投票:0回答:1

我在这里缺少什么?

当像这样向 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 上看到相同的行为。

perl ping strawberry-perl
1个回答
5
投票

这很有趣,一个类(构造函数)删除调用者的数据。

所示代码将引用传递给

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

而是使用本地副本。)

当子程序要处理具有大量数据的数据结构时,这种在调用方中更改数据的方法当然很有用,因为这样就不必复制它们。但是,当这不是子程序的目的时,我们需要小心,或者只是制作本地副本以确保安全。

© www.soinside.com 2019 - 2024. All rights reserved.