我是Perl的新手,目前正在学习Perl面向对象并且遇到了编写构造函数。
看起来当使用new
作为子例程的名称时,第一个参数将是包名。
构造函数必须使用关键字new
吗?或者是因为当我们使用packagename调用new
子例程时,传入的第一个参数是包名吗?
packagename->new;
当子程序有其他名称时,它将是第一个参数将是对象的引用?或者是因为当通过对象的引用调用子例程时,要传入的第一个参数将是对象的引用?
$objRef->subroutine;
答案 0 :(得分:11)
注意:以下所有示例都是为了教学目的而简化的。
是的,你是对的。您的new
函数的第一个参数, ,如果 作为方法调用,将是您调用它的内容。
调用方法有两种“风格”,但结果都是相同的。一种风味依赖于运算符,二元->
运算符。另一种味道依赖于参数的排序,方式bitransitive verbs work in English。大多数人只使用内置函数的dative / bitransitive样式 - 而或许使用内置函数,但很少使用其他内容。
在大多数(但不是全部)情况下,前两个是等价的:
<强> 1。 Dative 方法的调用
这是位置的,使用词序来确定发生了什么。
use Some::Package;
my $obj1 = new Some::Package NAME => "fred";
注意我们在那里没有使用方法箭头:没有写->
。这就是Perl本身使用的许多功能,例如
printf STDERR "%-20s: %5d\n", $name, $number;
几乎每个人都喜欢等同于:
STDERR->printf("%-20s: %5d\n", $name, $number);
然而,现在这种类型的dative调用几乎只用于内置函数,因为人们总是把事情搞得一团糟。
<强> 2。 箭头调用方法
箭头调用在很大程度上更清晰,更清晰,并且不太可能让你纠结在Perl解析奇怪的杂草中。注意我说不太可能;我做了不说它没有任何不足之处。但是,为了这个答案的目的,我们只是假装。
use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");
在运行时,除了任何奇特的奇怪或继承问题,实际的函数调用将是
Some::Package::new("Some::Package", "NAME", "fred");
例如,如果您在Perl调试器中并进行了堆栈转储,那么它的调用链就会像上一行一样。
由于调用方法始终使用invocant作为参数列表的前缀,因此将作为方法调用的所有函数都必须考虑该“额外”的第一个参数。这很容易做到:
package Some::Package;
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
这只是调用构造函数的最常见方法的极其简化的示例,以及内部发生的事情。在实际的生产代码中,构造函数会更加小心。
有时您在编译时不知道类名或方法名,因此您需要使用变量来保存其中一个或两个,或两者。编程中的间接与自然语言中的间接对象不同。间接只是意味着你有一个包含其他东西的变量,所以你使用变量来获取它的内容。
print 3.14; # print a number directly
$var = 3.14; # or indirectly
print $var;
我们可以使用变量来保存方法调用中涉及的仅涉及方法参数的其他内容。
第3。带有间接方法的箭头调用名称:
如果您不知道方法名称,则可以将其名称放在变量中。只能使用箭头调用来尝试此操作,而不是使用dative调用。
use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj = Some::Package->$action(NAME => "fido");
此处方法名称本身在运行时之前是未知的。
<强> 4。带有间接类的箭头调用名称:
这里我们使用一个变量来包含我们想要使用的类的名称。
my $class = (rand(2) < 1)
? "Fancy::Class"
: "Simple::Class";
my $obj3 = $class->new(NAME => "fred");
现在我们随机选择一个或另一个类。
你也可以这样使用dative调用:
my $obj3 = new $class NAME => "fred";
但通常不会使用用户方法。但有时会发生内置插件。
my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;
那是因为尝试在dative插槽中使用表达式通常不会在没有块的情况下工作;否则它只能是一个简单的标量变量,甚至不是数组或散列中的单个元素。
printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;
或更简单:
print { $fh{$filename} } "Some data.\n";
这很丑陋。
请注意,这并不完美。 dative对象槽中的文字与变量的工作方式不同。例如,使用 literal 文件句柄:
print STDERR;
装置
print STDERR $_;
但如果您使用间接文件句柄,请执行以下操作:
print $fh;
这实际上意味着
print STDOUT $fh;
这不太可能意味着你想要的东西,这可能是这样的:
print $fh $_;
又名
$fh->print($_);
关于方法调用箭头->
的事情是它不知道它的左操作数是表示类名的字符串还是表示对象实例的祝福引用。
当然,没有任何正式要求$class
包含包名称。它可能是,或者如果是这样,由方法本身来做正确的事情。
use Some::Class;
my $class = "Some::Class";
my $obj = $class->new(NAME => "Orlando");
my $invocant = (rand(2) < 1) ? $class : $obj;
$invocant->any_debug(1);
这需要一个非常花哨的any_debug
方法,根据它的调用者是否有福来做一些不同的方法:
package Some::Class;
use Scalar::Util qw(blessed);
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
sub any_debug {
my($invocant, $value) = @_;
if (blessed($invocant)) {
$invocant->obj_debug($value);
} else {
$invocant->class_debug($value);
}
}
sub obj_debug {
my($self, $value) = @_;
$self->{DEBUG} = $value;
}
my $Global_Debug;
sub class_debug {
my($classname, $value) = @_;
$Global_Debug = $value;
}
然而,这是一种相当先进和微妙的技术,仅适用于少数几种不常见的情况。在大多数情况下不建议这样做,因为如果处理不当可能会造成混淆 - 甚至可能是这样。
答案 1 :(得分:4)