Perl:如何深层复制一个受祝福的对象?

时间:2013-05-09 14:49:50

标签: perl oop deep-copy bless

我希望做一个深刻的(在这一点上,浅浅可能就足够了)一个受祝福的物体的副本。

Foo Class

package Foo;
our $FOO = new Foo;       # initial run

sub new {
   my $class = shift;
   my $self  = {};
   bless $self, $class;
   return $self;
}

主程序

use Foo;
my $copy = $Foo::FOO;     # instead of creating a ref, want to deep copy here
$copy->{bar} = 'bar';

bar同时出现在$Foo::FOO$copy中。我意识到我可以通过将其设置为$copy = { %{$Foo::FOO} }来创建对象的副本,但随后它将不再受到祝福;另外,这只适用于简单的数据结构(现在不是问题)。是这种方式复制然后保佑的唯一方法(例如$copy = bless { %{$Foo::FOO} }, q{Foo};)?

我正在努力避免使用Moose,Clone或其他非核心模块/软件包,因此请在回复时牢记这一点。 Bolded so stand stand更多:)

5 个答案:

答案 0 :(得分:11)

复制应该是API的一部分。模块的用户永远不会知道在创建新对象时需要执行哪些特殊操作(考虑在包中的my哈希中注册每个对象)。

因此,请为您的对象提供clone方法。在它里面,你可以使用你喜欢的任何肮脏技巧:

sub clone {
    my $self = shift;
    my $copy = bless { %$self }, ref $self;
    $register{$copy} = localtime; # Or whatever else you need to do with a new object.
    # ...
    return $copy;
}

答案 1 :(得分:9)

use Storable 'dclone';

$ corelist Storable

Storable was first released with perl v5.7.3

另外,你可以摆弄Storable钩子,以便更好地控制复制你的对象(不是我已经完成了,但这就是文档声称的内容)。

答案 2 :(得分:4)

my $copy = bless { %$self }, ref $self;是不够的。它只会克隆第一层。存储在$self中的任何引用都不会被克隆。这样做的后果是......

$obj->{ponies} = [qw(Dash Sparkle Jack)];
$clone = $obj->clone;
push @{$clone->{ponies}}, "Pinkie";
print join ", ", @{$obj->{ponies}};  # Dash Sparkle Jack Pinkie

您现在可能没有任何参考,但您可能会稍后。或者别人会把一个粘在你的物体上。或者他们将继承并添加一个。

您可以编写深度克隆例程,但这并不简单。我强烈建议使用Clone。它没有依赖关系,因此您只需将Clone.pm复制到项目中即可。

另一个选择是Storable::dclone,由@Zaid提及,长期以来一直处于核心地位。

无论你使用什么,在你的类上提供克隆方法是正确的,即使它只是Clone或Storable :: dclone的包装器。这将保护对象的用户免受克隆对象的详细信息的影响。

答案 3 :(得分:3)

调用程序没有好办法知道"复制对象"需要,所以一个对象应该知道如何复制自己。 Perl的OO在这里没有为您提供任何帮助,但传统的做法是这样的:

package Car;

sub clone {
    my ($self) = @_;

    return $self->new(
        ( map { $_ => $self->$_() } qw/make model/ ), # built-in types
        engine => $self->engine->clone(), # copying an object
    );
}

答案 4 :(得分:0)

对不起,我没注意到这句话:

* 我正在尝试避免使用Moose,Clone或其他非核心模块/软件包,因此在回复时请记住这一点。坚固,所以它更突出:) *

所以这个答案不能被接受!

#!/usr/bin/env perl -w
use strict;
use warnings;
use Storable;
use Data::Dumper;

my $src = {
  foo => 0,
  bar => [0,1]
};

$src -> {baz} = $src;
my $dst = Storable::dclone($src);
print '$src is : '.Dumper($src)."\n";
print '$dst is : '.Dumper($dst)."\n";