2011-09-19 12 views
5

好的。嘗試繼承任何子類的父類中設置的常量時遇到問題。用內聯包繼承常量

#!/usr/bin/perl 
use strict; 
use warnings; 

package Car; 
use Exporter qw(import); 
our @EXPORT_OK = ('WHEELS', 'WINGS'); 

use constant WHEELS => 4; 
use constant WINGS => 0; 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     colour => $args{colour}, 
     wheels => WHEELS, 
     wings => WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

package Car::Sports; 
use base qw(Car); 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     engine => $args{engine}, 
     wheels => WHEELS, 
     wings => WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

package main; 
my $obj = Car->new(doors => 4, colour => "red"); 
print Dumper $obj; 

my $obj2 = Car::Sports->new(doors => 5, engine => "V8"); 

print Dumper $obj2; 
__END__ 

的錯誤是:

Bareword "WHEELS" not allowed while "strict subs" in use at ./t.pl line 30. 
Bareword "WINGS" not allowed while "strict subs" in use at ./t.pl line 30. 
Execution of ./t.pl aborted due to compilation errors. 

現在,我還沒有來到這裏後沒有做一些研究。我明白,一個選項是use Car qw(WHEELS WINGS)Car::Sports。但是,如果我這樣做,我得到以下錯誤,因爲類是在同一文件中的所有內聯:

Can't locate Car.pm in @INC 

由於各種原因,我需要讓我的包在一個文件中。有沒有解決的辦法?由於常量基本上只是subs,所以爲什麼我必須導入它們,而對於普通方法來說,這不會是真的嗎?

最後,我也知道我可以做到這一點:

package Car::Sports; 
use base qw(Car); 

sub new { 
    my ($class, %args) = @_; 
    my $self = { 
     doors => $args{doors}, 
     engine => $args{engine}, 
     wheels => Car::WHEELS, 
     wings => Car::WINGS, 
    }; 
    bless $self, $class; 
    return $self; 
} 

而且它的罰款...但我有許多類,並希望使常數的繼承更通用的具有命名父類(有時它不只是父類,而是祖父母)。

非常感謝任何指針!

乾杯

回答

6

一個解決辦法是包括行

 
package Car::Sports; 
use base qw(Car); 
Car->import(qw(WHEELS WINGS)); 

使用印記在Car::Sports構造:

... 
wheels => &WHEELS, 
wings => &WINGS, 
... 

Car類沒有定義其@EXPORTS_OK列表直到運行時間。因爲Car::Sports構造函數在編譯時被解析,編譯器不知道Car::Sports命名空間中應該有WHEELSWINGS符號。


避免印記的唯一方法是在編譯時定義的Car出口:

package Car; 
our @EXPORT_OK; 
BEGIN {@EXPORT_OK = qw(WHEELS WINGS)} # set at compile not run time 
... 

package Car::Sports; 
use base qw(Car); 
BEGIN {Car->import('WHEELS','WINGS')} # import before c'tor is parsed 

您還可以通過在自己的定義Car類避免這些陰謀Car.pm文件。然後,你只想說

use Car qw(WHEELS WINGS); 

,一切都在Car.pm文件將在編譯時被解析,並Exporter::import法(以Car::import呼叫觸發)將自動獲得運行並導入所需的符號,以你目前的命名空間。

+0

完美!你已經很好地解釋了這一點並教會了我一些東西! – wawawawa

+0

此外,將Car :: Sports作爲Car的一個子類,可以使'Car :: Sports'訪問'Car''s *方法*,但不能像'&WHEELS'和'&WINGS'那樣使用*函數。調用'use base qw(Car)'是多餘的,直到你爲Car類增加更多的方法。 – mob

3

這種變化可能適合您的需求嗎?

[...] 
    wheels => $class->SUPER::WHEELS, 
    wings => $class->SUPER::WINGS, 
    [...] 

使用數據::自卸車你:

$VAR1 = bless({ 
      'wings' => 0, 
      'colour' => 'red', 
      'doors' => 4, 
      'wheels' => 4 
      }, 'Car'); 
$VAR1 = bless({ 
      'wings' => 0, 
      'engine' => 'V8', 
      'doors' => 5, 
      'wheels' => 4 
      }, 'Car::Sports'); 
+0

非常感謝...。我正在玩'SUPER',但沒有使用'$ class'。 – wawawawa

3

替代,你可以做什麼呢use

BEGIN { 
    package Car; 
    use Exporter qw(import); 
    @EXPORT_OK = qw(WHEELS); 

    ... 

    $INC{'Car.pm'} = 1; 
} 

BEGIN { 
    package Car::Sports; 

    use Car qw(WHEELS); 
    @ISA = 'Car'; 

    ... 

    $INC{'Car/Sports.pm'} = 1; 
} 
+0

ikegami - 我想我記得你來自Perlmonks!我很欣賞這些評論,謝謝! – wawawawa

0

一般情況下,暴露的東西是一個常數,任何包除了定義它的一個實際上是一個壞主意。除此之外,這還指出,在引用在您的代碼的其他區域中常常不變的值時,請不要使用不尋常的形式。

constant模塊實際上支持隱藏,我們正在談論的常數,因爲調用常量類的方法實際上是調用形式非常有效:

package Car; 
use constant default_wheel_count => 4; 

package Car::Sports; 

sub new { 
    my ($class) = @_; 

    return bless { 
     wheels => $class->default_wheel_count, 
    } => $class; 
} 

這是一個實際上是如何繼承常數,但它仍然可能是錯誤的方法。通過僅使用實現這些屬性構造的類中的常量來消除副本是實際正確的事情。