【问题标题】:Initializing an object in Perl在 Perl 中初始化对象
【发布时间】:2010-08-28 13:49:50
【问题描述】:

所以,我是一个 perl 新手。尽管我的事情要复杂得多,但我突然遇到了障碍,无法弄清楚 wtf 的代码有问题。我已经大大简化了它,它只是一小段代码。

Test.pl

package Test;

sub new {
  my ($class) = shift;
  my $self = {
    _attr => "asdfa"
  };
  bless $self, $class;
  return $self;
}
sub log {
  print "\nAccessed via class: ".$self->{_attr};
}

进程.pl

#!/usr/bin/perl
do "Test.pl";
use strict;
use warnings;
use diagnostics;

my($test) = new Test();
$test->log;
print "\nAccessed via main: ".$test->{_attr};

我运行 process.pl 并得到以下输出

通过类访问:
通过 main 访问:asdfa

我也收到警告

使用未初始化的值 Test.pl 处的连接 (.) 或字符串 第 12 行(#1) (W 未初始化)一个未定义的值被使用,就好像它已经被使用一样 定义。它被解释为“”或 0,但可能是一个错误。 要抑制此警告,请为您的变量分配一个定义的值。

所以问题是 $self 实际上是未定义的。为什么,我不知道。这不是初始化对象的方式吗?

【问题讨论】:

    标签: perl memory oop package


    【解决方案1】:

    对象实例作为第一个参数传递给方法。通常的做法是将其存储在一个名为$self 的变量中,但perl 不会为您设置这个,您必须自己设置:

    sub log {
      my $self = shift;
      print "\nAccessed via class: ".$self->{_attr};
    }
    

    请注意,尽管您在主代码中定义了 strictwarnings,但您还没有在 test.pl 中定义,这意味着 $self 是使用未定义值静默创建的,而不是在使用时引发编译错误一个未声明的变量。

    此外,我建议您将package Test 放入名为Test.pm 的文件中,在文件末尾添加一个真正的声明1;,并通过说use Test; 而不是@ 来调用它987654331@。这是在 Perl 中编写模块化代码的一种更简洁的方式。

    【讨论】:

      【解决方案2】:

      我知道您已经接受了答案,但您的代码存在一些严重问题。

      • 将对象定义放在以.pm 结尾的文件中,而不是.pl
      • use 模块而不是 doing 库。
      • 间接对象表示法会导致错误,最好避免使用它。

      我冒昧地重写了您的代码,并进行了修复问题所需的小改动。

      先在MyTest.pm

      package MyTest;  # Changed name to avoid name conflicts.
      use strict;      # Always
      use warnings;
      
      sub new {
        my $class = shift;
        # Removed parens on $class, they put the assignment of shift's 
        # result into list context (which had no effect on the result, 
        # but it is also unnecessary).
      
        my %defaults = ( attr => 'asdfa' );
        my %args = %defaults, @_;  
        # Assigns arguments and default values to %args, actual named 
        # args will override keys in defaults where they match;      
      
        my $self = {};      
      
        bless $self, $class;
      
        while( my ($attr, $value) = each %args ) {
          $self->$attr($value); # Initialize each attribute in named arg hash.
        }
      
        return $self;
      }
      
      sub attr {
          my $self = shift;
          if( @_ ) {
            $self->{_attr} = shift;
          }
      
          return $self->{attr}
      }    
      
      sub log {
        my $self = shift;  # Get invocant
        print "Accessed via class: ", $self->attr, "\n";  
      }
      

      进程.pl

      #!/usr/bin/perl
      use strict;
      use warnings;
      use diagnostics;
      
      use MyTest;
      
      my $test = MyTest->new();  # Direct call of object constructor
      
      $test->log;
      
      print "Accessed via main: $test->{_attr}\n";  # Violating encapsulation 
                                                    # is generally a bad idea.
      

      如果您正在执行繁重的 OOP,请考虑学习使用 Moose。 Moose 是一个强大的、超现代的 Perl 对象系统,它增加了强大的功能并减少了样板代码。

      如果你想学习“经典”Perl OOP,perldoc 中的教程(perlbootperltootperlobjperlbotperltooc)非常好。如果你想深入了解它,Damian Conway's Object Oriented Perl 是一本很棒的书。

      【讨论】:

      • 感谢您抽出宝贵时间彻底解释一切 daotoad。非常感谢。
      【解决方案3】:

      在子日志中:

      sub log{
          my $self = shift;
          print "\nAccessed via class: ".$self->{_attr};
      }
      

      【讨论】:

        猜你喜欢
        • 2013-05-15
        • 1970-01-01
        • 2018-02-10
        • 2013-06-27
        • 1970-01-01
        • 2021-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多