Perl中的->和::有什么区别?

25

在Perl中,::->有什么确切的区别?

->有时可以起到::无法实现的作用。


5
“->”用于解除引用;“::”用于引用其他包。你能举一个歧义的例子吗? 可以考虑以下情况:假设在一个程序中,有两个不同的包分别命名为A和B,并且它们都包含一个名为“foo”的函数。现在,如果使用以下代码 A::foo->(),这会导致歧义,因为无法确定是要调用A包中的foo函数并解除引用结果,还是引用B包中的foo函数。这种歧义可能会导致程序错误或运行不正常。 - fenway
3个回答

58

::有两种用法。

  1. 这是包名中的命名空间分隔符

     use Foo::Bar;    # 加载 Foo/Bar.pm
     $Foo::Bar::var   # 命名空间 Foo::Bar 中的 $var
    
  2. 附加到裸字上,它会创建一个字符串字面量[1]

    以下代码与 'hello' 相同,但如果包 hello 不存在,则会发出警告:

     hello::
    
->有两种用法。
  1. 它用于解引用。

     $array_ref->[$i]
     $hash_ref->{$k}
     $code_ref->(@args)
    
  2. 它用于方法调用中表示调用者。

     CGI->new()        # 静态方法调用
     $cgi->param()     # 对象方法调用
    

你可能在问什么是两者之间的区别。
Foo::Bar::mysub()

并且

Foo::Bar->mysub()

前者是一个函数调用。后者是一个方法调用。方法调用与函数调用有两个区别:
1. 方法调用使用继承。 2. 方法调用将调用者(剩下的“->”)作为第一个参数传递给子程序。

{
   package Foo::Baz;
   sub new {
      my ($class, $arg) = @_;
      my $self = bless({}, $class);
      $self->{arg} = $arg;
      return $self;
   }

   sub mysub1 {
      my ($self) = @_;
      print($self->{arg}, "\n");
   }
}

{
   package Foo::Bar;
   our @ISA = 'Foo::Baz'; 
   sub mysub2 {
      my ($self) = @_;
      print(uc($self->{arg}), "\n");
   }
}

my $o = Foo::Bar->new('hi');  # Same as: my $o = Foo::Baz::new('Foo::Bar', 'hi');
$o->mysub1();                 # Same as: Foo::Baz::mysub1($o);
$o->mysub2();                 # Same as: Foo::Bar::mysub2($o);

注意事项

  1. Foo->method 欺骗性地调用名为 Foo 的子程序(使用其返回值作为调用者)。Foo::->method,即 'Foo'->method,则不会。更多信息请参考这里

值得注意的是,调用 Foo::Bar->f() 会将隐式参数 'Foo::Bar' 传递给 $_[0],而 Foo::Bar::f() 则不会。 - el.pescado - нет войне
2
@el.pescado,这一点已经被注意到了(“方法调用将调用者(->后面的部分)作为其第一个参数传递给子程序。”),并得到了证明。 - ikegami

7
当右侧是一个函数时,-> 将其左侧传递为函数的第一个参数。如果 $foo 是一个被绑定到 Foo 包的对象,并且 Bar 在 Foo 包中,则以下示例是等效的。-> 将解析继承的方法,使其更加简洁和有用于对象。
$foo->Bar();

Foo::Bar($foo);

->也可以接受一个包名

Foo->Bar();

Foo::Bar('Foo');

这意味着->通常用于实例方法,以便对象被传递给自己和构造函数,以便构造函数知道要赋予哪个包。这通常是一个参数,因此它可以被继承。

@ikegami 澄清了继承中的行为。 - user1937198

-7

这里有很多解释,但对于新开发人员来说,以下是非常简单的答案:

FOO::BAR();  # is calling the class's (aka. package's) default object
$FOO->BAR(); # is calling an initiated object

一个对象通常具有经常设置的属性,而未初始化的对象仅使用默认对象属性。

假设FOO有一个名为“Age”的属性,它的默认值为1,我们可以通过程序中的set命令进行更改。然后我们决定再次调用包,以便我们可以看到两种方式:

use FOO;
$FOO = new FOO(); #new instance of foo 
$FOO->SetAge(21);

# more code here

print $FOO->GetAge(); # prints 21
print FOO::GetAge(); # prints 1

对于没有存储变量的包,怎么处理呢?在许多情况下可能根本没有区别,但这最终取决于类的编写方式。总之,这比较复杂...虽然这不是确切的答案,但我认为这是您根据问题所寻找的。

为了避免混淆,通常在创建对象时我不使用类/包名称。如果出于某种原因我不知道该如何称呼它,我会在前面加上“o”,以明确它是一个对象而不是一个类,这对于任何编程语言都是一个好习惯。

例如使用

$oFOO = new FOO(); // new object instance of foo

希望这有所帮助。

4
// 在 Perl 中不是注释符号。使用时应该写成 use FOO,而不是 using FOO - TLP
Que 是关于 Perl 而不是 Java - jkshah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接