我有3个使用Class::Std
声明为由内而外的Perl类。在这3个中的一个中,$basket{ident $self}
中存储了一个哈希引用,看起来像这样(Data :: Dumper的输出):
$VAR1 = {
'auto' => {
'items' => {
'abc' => bless( do{\(my $o = undef)}, 'Item' )
},
'obj' => bless( do{\(my $o = undef)}, 'Category' )
}
};
我需要获取此哈希引用,然后再次在其中创建所有内容(深度克隆)。我尝试像这样使用dclone
中的Storable
:
my $new_basket = dclone $basket{ident $self};
当我打印散列时,我得到了不同的内存地址:
print $new_basket, "\n";
print $basket{ident $self}, "\n";
print $new_basket->{auto}->{items}, "\n";
print $basket{ident $self}{auto}->{items}, "\n";
print $new_basket->{auto}->{items}->{abc}, "\n";
print $basket{ident $self}{auto}->{items}->{abc}, "\n";
这将输出:
HASH(0x55d325306a20)
HASH(0x55d325245298)
HASH(0x55d323b35ca8)
HASH(0x55d3243dd968)
Item=SCALAR(0x55d323b45190)
Item=SCALAR(0x55d325306588)
[当我不使用dclone
而改用my $new_basket = $basket{ident $self}
时,我得到的内存地址相同。当我使用my $new_basket = { %{ $basket{ident $self} } }
时,只能在第一层获得不同的地址,这应该是浅表副本。所有这一切似乎都很好并且可以预期。
所以,在我看来,dclone
实际上实际上是复制了所有内容,因为地址不同。但是,当我尝试在Item
中使用这样的方法时:
print $new_basket->{auto}->{items}->{abc}->get_added_on();
print $basket{ident $self}{auto}->{items}->{abc}->get_added_on();
我得到:
Use of uninitialized value in print at lib/Basket.pm line 231.
2020-05-30
显然dclone
的工作方式与我天真的想法不同。
我应该如何深度复制整个结构?我希望能获得一些帮助或对某些文章/文档的引用,在这里我可以阅读这里的内容。
一种解决方案是使用构造函数再次创建整个结构,但我想我会节省一些空间并使用dclone。显然结果不是很好。
EDIT:我被要求提供一个最小的可运行演示,在这里是:
#!/usr/bin/env perl
use strict;
use warnings;
{
package A;
use Class::Std;
use Data::Dumper;
use Storable qw(dclone);
my %basket :ATTR;
sub BUILD {
my ($self, $ident, $args_ref) = @_;
$basket{$ident}->{auto} = {};
my $c = C->new({ date => q{2020-05-30} });
$basket{$ident}->{auto}->{items}->{abc} = $c;
return;
}
sub deep_clone {
my $self = shift;
print Dumper $basket{ident $self};
# the next line prints "2020-05-30" as expected
print $basket{ident $self}->{auto}->{items}->{abc}->get_added_on();
my $new_basket = dclone $basket{ident $self};
# "Use of uninitialized value in print at ./deep-clone.pl line 35."
print $new_basket->{auto}->{items}->{abc}->get_added_on();
}
}
{
package C;
use Class::Std;
my %added_on :ATTR( :get<added_on> );
sub BUILD {
my ($self, $ident, $args_ref) = @_;
$added_on{$ident} = $args_ref->{date};
return;
}
}
####
my $a = A->new();
$a->deep_clone();
新创建的“ C”对象从未添加到%added_on
。
您的类将必须为Storable提供自定义处理程序来处理它们。
添加到“ A”:
sub STORABLE_freeze {
my ($self, $cloning) = @_;
my $ident = ident($self);
return "", {
basket => $basket{$ident},
# Other attributes...
};
}
sub STORABLE_thaw {
my ($self, $cloning, $serialized, $inner) = @_;
my $ident = ident($self);
$basket{$ident} = $inner->{basket};
# Other attributes...
}
添加到“ C”:
sub STORABLE_freeze {
my ($self, $cloning) = @_;
my $ident = ident($self);
return "", {
added_on => $added_on{$ident},
# Other attributes...
};
}
sub STORABLE_thaw {
my ($self, $cloning, $serialized, $inner) = @_;
my $ident = ident($self);
$added_on{$ident} = $inner->{added_on};
# Other attributes...
}
然后您可以毫无问题地使用freeze
/thaw
/ dclone
。
sub deep_clone {
my $self = shift;
#print Dumper $basket{ident $self};
CORE::say $basket{ ident $self }{auto}{items}{abc}->get_added_on();
my $clone = dclone($self);
#print Dumper $basket{ident $self};
CORE::say $basket{ ident $clone }{auto}{items}{abc}->get_added_on();
}