flope004 has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I have a problem with OO Perl (I don't have too much experience). My problem with a simple example (pretty closed to mine).

My object has two attributes: one is a ref. to
an anonymous array and one to anonymous hash. The second one is initialized from the first one. From the main program, I create 2 objects giving an array ref. The problem is when I create the second object I am also overwriting the hash from the first one How can I avoid this? What did I misunderstand from object creation?

Thanks!

The class

package dogandcat; use warnings; use strict; use Carp; # Class data and methods { # A list of all attributes with default values my %_attributes = ( _data => [], # anonymous array _hash => {}, # anonymous Hash ); # Return list of all attributes sub _all_attributes { keys %_attributes; } # Return the default value sub _attribute_default { my ( $self, $attribute ) = @_; $_attributes{$attribute}; } # Check if a given argument exists sub _arg_exist { my ( $self, %arg ) = @_; foreach my $arg ( keys %arg ) { unless ( grep /$arg/, keys %_attributes ) { croak("ERROR::: '$arg' is not a valid argument"); } } } } # Constructor sub new { my ( $class, %arg ) = @_; my $self = bless {}, $class; $self->_arg_exist(%arg); # Check if all given args are ok # Set the attributes for the provided arguments foreach my $attribute ( $self->_all_attributes() ) { my ($argument) = ( $attribute =~ /^_(.*)/ ); # Initilize to defaults $self->{$attribute} = $self->_attribute_default($attribute); # Override defaults with arguments if ( exists $arg{$argument} ) { $self->{$attribute} = $arg{$argument}; } } # Empty the hash loci %{$self->{_hash}} = (); # Getting the data from the array # and storing the information in the hash $self->_get_hash (); return $self; } # Accessors and Mutators sub DESTROY { my ($self) = @_; } sub _get_hash { my ($self) = @_; foreach my $locus (@{$self->{_data}}) { my @elements = split (/:/, $locus); if ( $elements[0] ne '' or scalar @elements < 3 ) { ${$self->{_hash}}{ $elements[0] } = $elements[1]; } else { croak ("ERROR"); } } } 1;

Main

Sorry for the bad practices

#!/usr/bin/perl use strict; use warnings; use dogandcat; my @cat = ("cat:a,b,c,d,e,f","cat2:g,h,i,j,k,l,m"); my @dog = ("dog1:1,2,3,4,5,6,7","dog2:21,22,23,24,25"); my $object1 = dogandcat->new (data =>\@cat); print "this is cat data \n", @{$object1->{_data}}, "\n"; print "this is cat keys \n", keys %{$object1->{_hash}}, "\n"; my $object2 = dogandcat->new (data => \@dog); print "this is dog data \n", @{$object2->{_data}}, "\n"; print "this is dog keys \n", keys %{$object2->{_hash}}, "\n"; print "\nbut....after creates the second object\n"; print "this is cat data \n", @{$object1->{_data}}, "\n"; print "this should be cat keys \n", keys %{$object1->{_hash}}, "\n"; print "this is dog data \n", @{$object2->{_data}}, "\n"; print "this is dog keys \n", keys %{$object2->{_hash}}, "\n"; exit;

Replies are listed 'Best First'.
Re: OO Perl: after it creates 2nd object overwrites hash from the first
by FunkyMonk (Bishop) on Aug 31, 2007 at 16:45 UTC
    Change
    %{$self->{_hash}} = ();

    to

    $self->{_hash} = {};

    Output:

    this is cat data cat:a,b,c,d,e,fcat2:g,h,i,j,k,l,m this is cat keys catcat2 this is dog data dog1:1,2,3,4,5,6,7dog2:21,22,23,24,25 this is dog keys dog1dog2 but....after creates the second object this is cat data cat:a,b,c,d,e,fcat2:g,h,i,j,k,l,m this should be cat keys catcat2 this is dog data dog1:1,2,3,4,5,6,7dog2:21,22,23,24,25 this is dog keys dog1dog2

       Thanks a lot!! It works fine!!
       Now I have to understand what's making the difference 
        Your version doesn't create a new hashref for each object. It just reuses the same one:
        my $self1 = {}; %{ $self1->{hash} } = (); print "$self1->{hash}\n"; %{ $self1->{hash} } = (); print "$self1->{hash}\n"; #Output: HASH(0x504f80) HASH(0x504f80)

        My use of {} creates a new hashref for both objects:

        my $self2; $self2->{hash} = {}; print "$self2->{hash}\n"; $self2->{hash} = {}; print "$self2->{hash}\n"; #Output: HASH(0x622bb0) HASH(0x504160)

Re: OO Perl: after it creates 2nd object overwrites hash from the first
by Roy Johnson (Monsignor) on Aug 31, 2007 at 17:09 UTC
    _attribute_default returns the same references every time, as the default attributes of new objects. Every $self->{_hash} refers to the same hash by default. You reassign the _data element by providing an initial value, but you don't reassign the _hash element.

    Watch it happen by adding some print statements to your constructor:

    # Constructor sub new { my ( $class, %arg ) = @_; my $self = bless {}, $class; $self->_arg_exist(%arg); # Check if all given args are ok # Set the attributes for the provided arguments foreach my $attribute ( $self->_all_attributes() ) { my ($argument) = ( $attribute =~ /^_(.*)/ ); # Initilize to defaults $self->{$attribute} = $self->_attribute_default($attribute); # Override defaults with arguments if ( exists $arg{$argument} ) { print STDERR "Overriding $attribute with $argument ($arg{$argument})\n +"; $self->{$attribute} = $arg{$argument}; } } foreach my $k (keys %$self) { print STDERR "$k: $self->{$k}\n"; } # Empty the hash loci %{$self->{_hash}} = (); # Getting the data from the array # and storing the information in the hash $self->_get_hash (); return $self; }

    Caution: Contents may have been coded under pressure.
Re: OO Perl: after it creates 2nd object overwrites hash from the first
by lyklev (Pilgrim) on Aug 31, 2007 at 23:20 UTC
    From your code you seem to think that perl objects are like c++-objects - which is not true. Perl-objects have no instance variables (or member variables or attributes); or at least not in the way you think. Variables you define within your package are class-variables, shared by all objects.

    The trick with objects is to 'bless' a reference (usually to a hash, containing the instance variables). Every time you call a member function, this reference gets added to your function arguments automagically. If this is a hash, the contents will contain your 'attributes'.

    I love Perl-objects and I think they are very useful so finding the time to get acquainted with objects would be a good idea.