top
Loading...
Perl的面向對象編程(四)
八、重載
  有時需要指定使用哪個類的方法,如兩個不同的類有同名方法的時候。假設類Espresso和Qava都定義了方法grind,可以用::操作符指定使用Qava的方法:
  $mess = Qava::grind("whole","lotta","bags");
  Qava::grind($mess, "whole","lotta","bags");
  可以根據程序的運行情況來選擇使用哪個類的方法,這可以通過使用符號引用去調用來實現:
  $method = $local ? "Qava::" : "Espresso::";
  $cup->{$method}grind(@args);
九、析構函數
  Perl跟蹤對象的鏈接數目,當某對象的最后一個應用釋放到內存池時,該對象就自動銷毀。對象的析構發生在代碼停止后,腳本將要結束時。對于全局變量而言,析構發生在最后一行代碼運行之后。
  如果你想在對象被釋放之前獲取控制權,可以定義DESTROY()方法。DESTROY()在對象將釋放前被調用,使你可以做一些清理工作。DESTROY()函數不自動調用其它DESTROY()函數,Perl不做內置的析構工作。如果構造函數從基類多次bless,DESTROY()可能需要調用其它類的DESTROY()函數。當一個對象被釋放時,其內含的所有對象引用自動釋放、銷毀。
  一般來說,不需要定義DESTROY()函數,如果需要,其形式如下:


sub DESTROY {
#
# Add code here.
#
}

  因為多種目的,Perl使用了簡單的、基于引用的垃圾回收系統。任何對象的引用數目必須大于零,否則該對象的內存就被釋放。當程序退出時,Perl的一個徹底的查找并銷毀函數進行垃圾回收,進程中的一切被簡單地刪除。在UNIX類的系統中,這像是多余的,但在內嵌式系統或多線程環境中這確實很必要。
十、繼承
  類方法通過@ISA數組繼承,變量的繼承必須明確設定。下例創建兩個類Bean.pm和Coffee.pm,其中Coffee.pm繼承Bean.pm的一些功能。此例演示如何從基類(或稱超類)繼承實例變量,其方法為調用基類的構造函數并把自己的實例變量加到新對象中。
  Bean.pm代碼如下:


package Bean;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(setBeanType);

sub new {
my $type = shift;
my $this = {};
$this->{"Bean"} = "Colombian";
bless $this, $type;
return $this;
}

#
# This subroutine sets the class name
sub setBeanType{
my ($class, $name) = @_;
$class->{"Bean"} = $name;
print "Set bean to $name n";
}
1;

  此類中,用$this變量設置一個匿名哈希表,將"Bean"類型設為"Colombian"。方法setBeanType()用于改變"Bean"類型,它使用$class引用獲得對對象哈希表的訪問。
  Coffee.pm代碼如下:


1 #
2 # The Coffee.pm file to illustrate inheritance.
3 #
4 package Coffee;
5 require Exporter;
6 require Bean;
7 @ISA = qw(Exporter, Bean);
8 @EXPORT = qw(setImports, declareMain, closeMain);
9 #
10 # set item
11 #
12 sub setCoffeeType{
13  my ($class,$name) = @_;
14  $class->{"Coffee"} = $name;
15  print "Set coffee type to $name n";
16  }
17 #
18 # constructor
19 #
20 sub new {
21  my $type = shift;
22  my $this = Bean->new(); ##### <- LOOK HERE!!! ####
23  $this->{"Coffee"} = "Instant"; # unless told otherwise
24  bless $this, $type;
25  return $this;
26  }
27 1;

  第6行的require Bean;語句包含了Bean.pm文件和所有相關函數,方法setCoffeeType()用于設置局域變量$class->{"Coffee"}的值。在構造函數new()中,$this指向Bean.pm返回的匿名哈希表的指針,而不是在本地創建一個,下面兩個語句分別為創建不同的哈希表從而與Bean.pm構造函數創建的哈希表無關的情況和繼承的情況:
  my $this = {}; #非繼承
  my $this = $theSuperClass->new(); #繼承
  下面代碼演示如何調用繼承的方法:


1 #!/usr/bin/perl
2 push (@INC,"pwd");
3 use Coffee;
4 $cup = new Coffee;
5 print "n -------------------- Initial values ------------ n";
6 print "Coffee: $cup->{"Coffee"} n";
7 print "Bean: $cup->{"Bean"} n";
8 print "n -------------------- Change Bean Type ---------- n";
9 $cup->setBeanType("Mixed");
10 print "Bean Type is now $cup->{"Bean"} n";
11 print "n ------------------ Change Coffee Type ---------- n";
12 $cup->setCoffeeType("Instant");
13 print "Type of coffee: $cup->{"Coffee"} n";

  該代碼的結果輸出如下:


-------------------- Initial values ------------
Coffee: Instant
Bean: Colombian
-------------------- Change Bean Type ----------
Set bean to Mixed
Bean Type is now Mixed
------------------ Change Coffee Type ----------
Set coffee type to Instant
Type of coffee: Instant

  上述代碼中,先輸出對象創建時哈希表中索引為"Bean"和"Coffee"的值,然后調用各成員函數改變值后再輸出。
  方法可以有多個參數,現在向Coffee.pm模塊增加函數makeCup(),代碼如下:


sub makeCup {
my ($class, $cream, $sugar, $dope) = @_;
print "n================================== n";
print "Making a cup n";
print "Add cream n" if ($cream);
print "Add $sugar sugar cubesn" if ($sugar);
print "Making some really addictive coffee ;-) n" if ($dope);
print "================================== n";
}

  此函數可有三個參數,不同數目、值的參數產生不同的結果,例如:


1 #!/usr/bin/perl
2 push (@INC,"pwd");
3 use Coffee;
4 $cup = new Coffee;
5 #
6 # With no parameters
7 #
8 print "n Calling with no parameters: n";
9 $cup->makeCup;
10 #
11 # With one parameter
12 #
13 print "n Calling with one parameter: n";
14 $cup->makeCup("1");
15 #
16 # With two parameters
17 #
18 print "n Calling with two parameters: n";
19 $cup->makeCup(1,"2");
20 #
21 # With all three parameters
22 #
23 print "n Calling with three parameters: n";
24 $cup->makeCup("1",3,"1");

  其結果輸出如下:


Calling with no parameters:
==================================
Making a cup
==================================
Calling with one parameter:
==================================
Making a cup
Add cream
==================================
Calling with two parameters:
==================================
Making a cup
Add cream
Add 2 sugar cubes
==================================
Calling with three parameters:
==================================
Making a cup
Add cream
Add 3 sugar cubes
Making some really addictive coffee ;-)
==================================

  在此例中,函數makeCup()的參數既可為字符串也可為整數,處理結果相同,你也可以把這兩種類型的數據處理區分開。在對參數的處理中,可以設置缺省的值,也可以根據實際輸入參數值的個數給予不同處理。
十一、子類方法的重載
  繼承的好處在于可以獲得基類輸出的方法的功能,而有時需要對基類的方法重載以獲得更具體或不同的功能。下面在Bean.pm類中加入方法printType(),代碼如下:


sub printType {
my $class = shift @_;
print "The type of Bean is $class->{"Bean"} n";
}

  然后更新其@EXPORT數組來輸出:
  @EXPORT = qw ( setBeanType , printType );
  現在來調用函數printType(),有三種調用方法:


$cup->Coffee::printType();
$cup->printType();
$cup->Bean::printType();

  輸出分別如下:


The type of Bean is Mixed
The type of Bean is Mixed
The type of Bean is Mixed

  為什么都一樣呢?因為在子類中沒有定義函數printType(),所以實際均調用了基類中的方法。如果想使子類有其自己的printType()函數,必須在Coffee.pm類中加以定義:


#
# This routine prints the type of $class->{"Coffee"}
#
sub printType {
my $class = shift @_;
print "The type of Coffee is $class->{"Coffee"} n";
}

  然后更新其@EXPORT數組:
  @EXPORT = qw(setImports, declareMain, closeMain, printType);
  現在輸出結果變成了:


The type of Coffee is Instant
The type of Coffee is Instant
The type of Bean is Mixed

  現在只有當給定了Bean::時才調用基類的方法,否則直接調用子類的方法。
  那么如果不知道基類名該如何調用基類方法呢?方法是使用偽類保留字SUPER::。在類方法內使用語法如:$this->SUPER::function(...argument list...); ,它將從@ISA列表中尋找。剛才的語句用SUPER::替換Bean::可以寫為$cup->SUPER::printType(); ,其結果輸出相同,為:


The type of Bean is Mixed
北斗有巢氏 有巢氏北斗