北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

blessは2引数で

わかっててわざとやるんだったらいいんですけど、そうじゃないなら定石通りに書くべきだと思います。

  5   sub new {
  6     my $c = shift;
  7     bless $c;
  8   }

 33   my $human = Human::new({'name' => 'ビンゴ',
 34                           'age'  => 1,
 35                           'bag'  => {}
 36                         });

第31回 Perlでオブジェクト指向を書こう

この書き方の問題点は二つあります。まず、コンストラクタを Human::new のように静的呼び出ししていること。もう一つが、blessを1引数で使っていることです。

確かにblessは1引数で使っても、デフォルトとして定義された時点でのパッケージへリファレンスを紐づけてくれます。ってことは、わざわざクラス名が渡るように書かない方が、冗長じゃなくていいじゃん? と思われるかもしれないですが、それはちょっと早計です。


なぜなら、この書き方では継承ができないからです。継承しようと頑張って以下のコードを書いてみても、うまく行きません。

package Hero;
use base qw/Human/;

*new = \&Human::new;

sub get_old {
    warn "The hero doesn't get older.";
}

package main;
my $human = Hero::new( ... 略 ...);

まず、newを静的呼び出ししているために、せっかくHumanクラスを継承してもHeroからHumanのnewが探索されません。そのため、Humanクラスでもnew関数を用意する(*new = の部分)必要があります。

そして、blessに第二引数を渡していないため、Hero::new からは Hero ではなく Humanに紐づいたオブジェクトが返ってきます。そのため、オーバーライドした get_old が呼ばれません。

以下がきちんと継承できる書き方となります。

package Human;
sub new {
    my $class  = shift;
    my ($ref_param) = @_;
    bless $ref_param, $class;
}

... 色々略 ...

package Hero;
use base qw/Human/;

sub get_old {
    warn "The hero doesn't get older.\n";
}

package main;
my $human = Hero->new({ .. 略 .. });

... 色々略 ...

こう書くことで、サブクラス名 'Hero' が Human::new まで渡って行き、 Human::new 内でうまく 'Hero' に bless されるようになります。


なお、perlmodlib的には、newは以下のように書くべきとなってます。*1

Use the two argument form of bless to bless into the class name given as the first parameter of the constructor, e.g.,:

(.. 中略 ..)

 sub new {
     my $self  = shift;
     my $class = ref($self) || $self;
     return bless {}, $class;
 }

*1:ただしperl - 万能なnewの書き方にもある通りPBP的にはこのコードはNG。