Perl 类的定义

Perl的一个packag可以作为一个类使用,文件后缀名为.pm,并且把package里的函数当作类的方法来用。如:

package Person;

创建和使用对象

大多数程序使用类名作为构造函数,Perl 中可以使用任何名字。

你可以使用多种 Perl 的变量作为 Perl 的对象。大多数情况下我们会使用引用数组或哈希。

接下来我们为 Person 类创建一个构造函数,使用了 Perl 的哈希引用。如:

package Person;
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# 输出用户信息
print "名字:$self->{_firstName}\n";
print "姓氏:$self->{_lastName}\n";
print "编号:$self->{_ssn}\n";
bless $self, $class;
return $self;
}

 接下来我们创建一个对象:

$object = new Person( "小明", "王", 23234345);

 也可以使用如下方法创建对象:

$object = Person->new( "小明", "王", 23234345);

定义方法

Perl类的方法只是个Perl子程序而已,也即通常所说的成员函数。

Perl面向对象中Perl的方法定义不提供任何特别语法,但规定方法的第一个参数为对象或其被引用的包。

Perl 没有提供私有变量,但我们可以通过辅助的方式来管理对象数据。(比如通过方法中的第一个参数也就是对象或者引用的包的哈希来保存私有变量),如下所示:

sub setFirstName {
my ( $self, $firstName ) = @_;
$self->{_firstName} = $firstName if defined($firstName);
return $self->{_firstName};
}

修改Person.pm 如下:

#!/usr/bin/perl 

package Person;

sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# 输出用户信息
print "名字:$self->{_firstName}\n";
print "姓氏:$self->{_lastName}\n";
print "编号:$self->{_ssn}\n";
bless $self, $class;
return $self;
}
sub setFirstName {
my ( $self, $firstName ) = @_;
$self->{_firstName} = $firstName if defined($firstName);
return $self->{_firstName};
} sub getFirstName {
my( $self ) = @_;
return $self->{_firstName};
}
1; 

Perl继承

Perl 里 类方法通过@ISA(Perl内置数组)数组继承,这个数组里面包含其他包(类)的名字,变量的继承必须明确设定。

多继承就是这个@ISA数组包含多个类(包)名字。

通过@ISA只能继承方法,不能继承数据。但是EXPORT可以是方法,也可以是数据,参见文章“Perl中神奇的@EXPORT”

接下来我们创建一个 Employee 类继承 Person 类。

#!/usr/bin/perl

package Employee;
use Person;
use strict;
our @ISA = qw(Person); # 从 Person 继承 # 重写构造函数
sub new {
my ($class) = @_; # 调用父类的构造函数
my $self = $class->SUPER::new( $_[1], $_[2], $_[3] );
# 添加更多属性
$self->{_id} = undef;
$self->{_title} = undef;
bless $self, $class;
return $self;
} # 重写方法
sub getFirstName {
my( $self ) = @_;
# 这是子类函数
print "这是子类函数\n";
return $self->{_firstName};
} # 添加方法
sub setLastName{
my ( $self, $lastName ) = @_;
$self->{_lastName} = $lastName if defined($lastName);
return $self->{_lastName};
} sub getLastName {
my( $self ) = @_;
return $self->{_lastName};
} 1;

当在类中找不到某个实例方法时,它就会检查该类的 @ISA 是否被初始化。如果已经初始化了,它检查其中的某个模块是否支持这个“缺少”的函数。如果它按照深度优先的层次结构搜索 @ISA 数组并且发现同名的方法,它会调用第一个被发现的同名方法并将控制权交给它。我们利用 Perl 语言的这个特性实现了继承。

如上所示继承后子类增加了自己的方法,并且在子类中对方法进行了重写。

     这里我们注意到在子类的构造函数中我们通过SUPER调用了基类的构造函数:

my $self = $class->SUPER::new( $_[1], $_[2], $_[3] );

  我们也可以直接使用基类的类名来调用:

my $self = Person::new( $_[1], $_[2], $_[3] );

    如果只知道父类的方法名,而不知道父类名,我们可以使用伪类保留字SUPER::

总结

通过在子类中use 基类,并将基类名放在内置数组@ISA中即可实现继承:

#!/usr/bin/perl

package Employee;

use Person;
use strict;
our @ISA = qw(Person); # 从 Person 继承

 

Perl 中的bless

从前面的代码中可以看到Perl类的构造函数使用了bless 函数。下面介绍其由来。

Perl不像其他的面向对象语言具有变量修饰符(public, private ,final...). 类属性只是包中的全局变量,而类方法则是不依赖于任何特定实例的普通子例程。如下为类方法和类属性的例子:

package Person; 

my $person_number = 0;

sub new {
my $self = {};
shift;
my ($name, $age) = @_;
$self->{name} = $name;
$self->{age} = $age;
$person_number++;
bless ($self);
return $self;
} sub calculate_person_number { return $person_number; } my $object_personA = Person->new ( “ David ” , 27); my $object_personB = Person::new ( “ Tonny ” , 27); my $person_number = Person::calculate_person_number (); print “ We have ” . $person_number . “ persons in all. \n ” ;

     因此,不能将实例的变量直接放在整个package中(否则将成为类变量)。我们只能这么做:

package Person;
sub new {
my ($name, $age) = @_;
my $r_object = {
“ name ” => $name,
“ age ” => $age
} return $r_object; } my $personA = Person->new ( “ Tommy ” , 22 ); my $personB = Person->new ( “ Jerry ” , 30 ); print “ Person A ’ s name: ” . $personA->{name} . “ age: ” . $personA->{age} . ” .\n ” ; print “ Person B ’ s name: ” . $personB->{name} . “ age: ” . $personB->{age} . ” .\n ” ;

  那么问题来了:Perl 的编译器并不知道new 函数所返回的指向匿名哈希表的引用属于哪个类(模块)。这样的话,如果要使用类中的实例方法,只能直接标出方法所属于的类(模块)的名字,并将引用作为方法的第一个参数传递给它,如下所示:

package Person;
… sub change_name {
my ($self, $new_name) = @_;
$self->{name} = $new_name; } my $object_person = Person->new ( “ Tommy ” , 22); print “ Person ’ s name: ” . $object_person->{name} . “ .\n ” ; Person::change_name ($object_person, “ Tonny ” ); print “ Person ’ s new name: ” . $object_person->{name} . “ .\n ” ;

  对于这个问题,Perl 中的 bless 函数提供了一个解决问题的桥梁。 bless 以一个普通的指向数据结构的引用为参数,它将会把那个数据结构(注意:此处不是引用本身)标记为属于某个特定的包,这样就赋予了这个匿名哈希表的引用以多态的能力。同时,我们使用箭头记号来直接调用那些实例方法,如下所示:

package Person
my $person_number = 0;
sub new {
my $self = {};
shift;
my ($name, $age) = @_;
$self->{name} = $name;
$self->{age} = $age;
bless ($self);
return $self;
} sub change_name {
my $self = shift;
my $name = shift;
$self->{name} = $name;
} sub getName {
my $self = shift;
return $self->{name};
} sub calculate_person_number { return $person_number; }
my $object_person = Person->new ( “ David ” , 27); print “ Name: “ . $object_person->{name} . “ \n ” ; $object_person->change_name ( “ Tony ” ); print “ Name: “ . $object_person->{name} . “ \n ” ; my $object_personA = Person->new ( “ David ” , 27); my $object_personB = Person::new ( “ Tonny ” , 27); my $person_number = Person::calculate_person_number (); print “ We have ” . $person_number . “ persons in all. \n ” ;

  如上代码 中的“ bless ($self) ”,将指向一个匿名哈希表的引用标记为属于当前包,也就是 package Person 。

所以,当 Perl 看到“ $object_person->change_name ($name) ”时,它会决定 $object_person 属于 package Person 。 Perl 就会如下所示地调用这个函数,“ Person::change_name ($object_person, $name) "。

换而言之,如果使用箭头的方式调用一个函数,箭头左边的那个对象将作为相应子例程的第一个参数。 Perl 的实例方法的本质其实就是一个第一个参数碰巧为对象引用的普通子例程。

与实例方法不同,我们使用 Person::calculate_person_number () 的形式来调用类方法。这样的话,指向匿名哈希表的引用将不会作为第一个调用参数传入。

(从这里也可以看出::和->使用的区别,一个是类方法调用,一个是实例方法调用)

一般的bless是使用两个参数的,如下:

sub new {
my ($class) = @_; # 调用父类的构造函数
my $self = $class->SUPER::new( $_[1], $_[2], $_[3] );
# 添加更多属性
$self->{_id} = undef;
$self->{_title} = undef;
bless $self, $class;
return $self;
}

  当第二个参数省略时默认为当前包。

参考文献:

http://www.cnblogs.com/A-Song/archive/2012/04/12/2443541.html

http://blog.sina.com.cn/s/blog_6151984a0100eq6e.html

http://www.runoob.com/perl/perl-object-oriented.html

Perl 的继承的更多相关文章

  1. Perl 和 Python 的比较 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&id=4662991&uid=608135 作为万年Perl 党表示最近开 ...

  2. perl面向对象

    来源: http://www.cnblogs.com/itech/archive/2012/08/21/2649580.html Perl面向对象     首先让我们来看看有关 Perl 面向对象编程 ...

  3. Perl中神奇的@EXPORT

    @EXPORT Perl通过继承,可以使子类可以像使用本地方法一样使用其基类的方法. 一个类如果想把自己的方法(变量)暴露给别人使用(比如一些公共基础类的的通用方法或变量),还可将直接将方法(变量)添 ...

  4. Ruby. Vs . Python

    前言:从语言的本质上来分析,我对Ruby持反对态度,毕竟语言是为了交流,在表达的效率层面为了正确性必须适当放弃复杂性.且有句老话说的好,Ruby In Rails 才是语言,而Ruby只是这个语言的工 ...

  5. perl 继承 @ISA

    12.5 类继承 对Perl的对象剩下的内容而言,从一个类继承另外一个类并不需要给这门语法增加特殊的语法,当你调用一个方法的时候, 如果Perl在调用者的包里找不到这个字过程,那么他就检查@ISA数组 ...

  6. perl 继承概述

    <pre name="code" class="html">[root@wx03 test]# cat Horse.pm package Horse ...

  7. perl use base 继承

    centos6.5:/root/podinns/lib#cat First.pm package First; use base qw(Second); sub new { my $self = {} ...

  8. perl 继承小例子

    <pre name="code" class="html"><pre name="code" class="ht ...

  9. perl 继承写法

    use base (Critter); 和 BEGIN{ require Critter; @ISA=qw/Critter/; } 这两种写法是等价

随机推荐

  1. aspcms安装所遇到的问题

     aspcms标签:http://biaoqian.iasp.com.cn/ 1.报错:An error occurred on the server when processing the URL. ...

  2. 【NS2】TCL debug (转载)

    1.使用NS2进行模拟,就不可避免的会接触TCL/OTCL和C/C++.两者配合使用.一般设置场景啊,业务流啊,都使用TCL/OTCL来编写脚 本.要进行路由实验模拟的话,同一类的实验,这些脚本基本上 ...

  3. @topcoder - 2017TCOAlgorithmRound2A - D1L2@ DistanceZeroAndOne

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 一个 n 个点的无向简单的连通图,编号从 0 到 n-1. 现给 ...

  4. P2P四红线

    P2P四红线 刘张君指出,P2P网络借贷平台是一种新兴金融业态,在鼓励其创新发展的同时,要记住四点:一是要明确这个平台的中介性质,二是要明确平台本身不得提供担保,三是不得将归集资金搞资金池,四是不得非 ...

  5. @loj - 6353@「CodePlus 2018 4 月赛」组合数问题 2

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 请你找到 k 个不同的组合数,使得对于其中任何一个组合数 \(C ...

  6. 在ThinkPHP中,if标签和比较标签对于变量的比较。

    在TP模板语言中.if和eq都可以用于变量的比较. <比较标签 name="变量" value="值">内容</比较标签> 比如: &l ...

  7. DirectEvents用法

    DirectEvents异步执行服务器端事件 我们首先来看一下Ext.Net DirectEvents的一个最简单用法,通过点击按钮触发服务器端的事件处理方法,并在前台弹出一个提示框. <ext ...

  8. H3C 配置CHAP验证

  9. H3C 什么是OSPF

  10. H5 操作class 类样式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...