为啥perl对象实例会互相覆盖
Posted
技术标签:
【中文标题】为啥perl对象实例会互相覆盖【英文标题】:Why does perl object instance overwrite each other为什么perl对象实例会互相覆盖 【发布时间】:2013-07-02 14:33:21 【问题描述】:我已经编写了一些 Perl 代码,这些代码由基础类所固有的两个类组成。我想它会打印出这样的东西
Mik: Meow! Meow!
Sat: Woof! Woof!
但它实际上是这样打印的:
Sat: Woof! Woof!
Sat: Woof! Woof!
,
package Animal;
sub new
my $obj = shift;
my $name = shift;
our %pkg = ( 'name' => $name );
bless \%pkg, $obj;
return \%pkg;
package Cat;
@ISA = ("Animal");
sub new
my $obj = shift;
my $name = shift;
my $self = $obj->SUPER::new($name);
return $self;
sub get_name
my $obj = shift;
return $obj->'name';
sub talk
my $obj = shift;
return "Meow! Meow!";
package Dog;
@ISA = ("Animal");
sub new
my $obj = shift;
my $name = shift;
my $self = $obj->SUPER::new( $name );
return $self;
sub get_name
my $obj = shift;
return $obj->'name';
sub talk
my $obj = shift;
return "Woof! Woof!";
package Main;
my $cat = new Cat('Mike');
my $dog = new Dog('Sat');
print $cat->get_name() . ": " . $cat->talk() , "\n";
print $dog->get_name() . ": " . $dog->talk() , "\n";
但如果我以这种方式更改调用者,它会打印出我认为的内容。所以很奇怪为什么$cat
对象在$dog
被实例化之后被覆盖了?
package Main;
my $cat = new Cat('Mily');
print $cat->get_name() . ": " . $cat->talk() , "\n";
my $dog = new Dog('Sat');
print $dog->get_name() . ": " . $dog->talk() , "\n";
【问题讨论】:
【参考方案1】:为什么要祝福成一个全局变量?将您的构造函数更改为:
sub new
my $obj = shift;
my $name = shift;
my %pkg = ( 'name' => $name );
bless \%pkg, $obj;
return \%pkg;
更好的是,把它改成更惯用的东西:
sub new
my $class = shift;
my $name = shift;
my $self = name => $name ;
return bless $self, $class;
继续:
为什么要在每种动物中都使用new
和get_name
?这两种方法都可以继承。当我们这样做的时候,我们不妨摆脱 @ISA
的麻烦:
package Animal;
sub new
my $class = shift;
my $name = shift;
my $self = name => $name ;
return bless $self, $class;
sub get_name
my $self = shift;
return $self->'name';
package Cat;
use base qw/ Animal /;
sub talk
my $self = shift;
return "Meow! Meow!";
package Dog;
use base qw/ Animal /;
sub talk
my $self = shift;
return "Woof! Woof!";
package Main;
my $cat = Cat->new('Mike');
my $dog = Dog->new('Sat');
print $cat->get_name() . ": " . $cat->talk() , "\n";
print $dog->get_name() . ": " . $dog->talk() , "\n";
请问您正在学习哪本教程或书籍?
虽然上述方法非常好,但您不妨使用 Modern Perl 的方式:
package Animal;
use Moose;
has name => ( required => 1, is => 'rw', isa => 'Str' );
package Cat;
use Moose;
extends 'Animal';
has talk => ( default => "Meow! Meow!", is => 'ro' );
package Dog;
use Moose;
extends 'Animal';
has talk => ( default => "Woof! Woof!", is => 'ro' );
package Main;
my $cat = Cat->new( name => 'Mike');
my $dog = Dog->new( name => 'Sat');
print $cat->name . ": " . $cat->talk , "\n";
print $dog->name . ": " . $dog->talk , "\n";
【讨论】:
嗯,是的。好的 OO 设计总是很难;语言所能做的最好的事情就是不妨碍你。但我认为 Moose 让你更容易感受到设计的正确性。以上面的例子为例。talk
使用了一个方法,但它实际上只是一个属性。因此,它应该是基类中的一个方法,它使用子类中的另一个属性。我发现很难以老式的形式看到这样的东西。
@innaM 非常感谢您的帮助。让我解释一下。 1. 我的 Dog 实例覆盖了我的 Cat 我真的很困惑,所以我想检查每个类的详细信息。我并不是真的要在每个类中定义 get_name 。 2. 我曾经使用过很多非常旧的 Unix 机器,所以我必须在 Perl 5.8 中编写代码,并且必须避免编写任何比它更新的东西。 :-( . 3. 专门针对这个问题,它是我在网络某处遇到的一个 Python 程序,我想翻译成 Perl 程序。但不幸的是滑倒了它。【参考方案2】:
简而言之:our
声明了包变量,因此每次执行 our %pkg = (...)
时,都会为同一个变量分配一个新值。由于所有\%pkg
引用都指向同一个var,所以new
的所有返回值都是同一个对象。一个引用只能被祝福到一个类中,所以最后一个获胜。
只需将our
更改为my
,它应该可以按预期工作。
【讨论】:
【参考方案3】:您已经声明了用于存储实例数据的变量
our %pkg
这是单个数据结构 (%Animal::pkg
) 的别名,因此您的所有对象都使用相同的哈希。将our
更改为my
,以便每次都创建一个新的哈希。
可能值得注意的是,Perl 中的“由内而外”对象可以并且确实使用包中的共享数据结构来存储实例数据,但需要额外的抽象级别才能使其工作,我不会'不建议从它们开始 OO Perl,它们是后天习得的。
【讨论】:
以上是关于为啥perl对象实例会互相覆盖的主要内容,如果未能解决你的问题,请参考以下文章