Getting object properties from a pointer stored in a vector
Compact description
I'm having a problem to find out what's wrong, for some obscure reason the properties in an object for which I stored the pointer in a vector seem to be changed.
Detailed explanation
I have a class Rabbit which looks like this:
class Rabbit {
enum sexes { MALE = 0x1, FEMALE = 0x2 } ;
in开发者_StackOverflow社区t sex ;
bool has_mated ;
Rabbit();
~Rabbit();
void setSexe(int sex);
void match( vector<Rabbit*> &rabbits );
void breed( Rabbit &partner, vector<Rabbit*> &rabbits );
}
For now it's a very basic class, the destructor is still empty and it has a few properties.
I also have a pointer vector of type vector<Rabbit*>
vector<Rabbit*> rabbits = vector<Rabbit*>(0);
which I use to store pointers to newly created rabbits. I'm passing the pointers to newly created rabbits to that vector like this.
Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ; //I think we don't need the pointer anymore as we copied it to the vector
my intention is to free memory whenever a rabbit gets popped off like this. ( I hope this is the right way )
Rabbit* dead_rabbit = rabbits.back(); //obtain the pointer
delete dead_rabbit ; //free the associated memory
rabbits.pop_back(); //delete the pointer itself
But I run into trouble when I try to access the sex property of a rabbit for which the pointer has been stored in the vector.
Rabbit* rabbit_p = rabbits.at(r) ;
cout << rabbit_p->sex << endl ; // prints a verry high number instead of 1 or 2
So my question is why does this happen, am I unknowingly referring to another place in the heap and reading out another value? and why?
Below I'll include the whole source code, it is far from correct rabbit breading behavior, but I wanted to test dynamic memory assignment for objects. At first the vector contained just plain rabbits, but the memory wasn't released, so now I'm testing the pointer approach.
Complete source
using namespace std ;
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iterator>
#include <sys/time.h>
#include <sys/resource.h>
class Rabbit {
public:
enum sexes { MALE = 0x1, FEMALE = 0x2 } ;
int sex ;
bool has_mated ;
Rabbit();
~Rabbit();
void setSexe(int sex);
void match( vector<Rabbit*> &rabbits );
void breed( Rabbit &partner, vector<Rabbit*> &rabbits );
};
Rabbit::Rabbit(){
this->sex = random() % 2 + 1 ; //random m/f
this->has_mated = false ;
}
Rabbit::~Rabbit(){
}
void Rabbit::setSexe( int sex ){
this->sex = sex ;
}
void Rabbit::match(vector<Rabbit*> &rabbits){
int s = rabbits.size() ;
int r = 0 ;
for(r ; r < s ; r++ ){
Rabbit* partner_ptr = rabbits.at(r) ;
Rabbit partner = *partner_ptr ;
if( partner.sex == Rabbit::MALE && partner.has_mated == false ){
this->breed(partner, rabbits);
this->has_mated = true ;
partner.has_mated = true ;
break ;
}
}
}
void Rabbit::breed( Rabbit &partner, vector<Rabbit*> &rabbits ){
int offspring, sex ;
offspring = random() % 4 + 3 ;
cout << "breeding " << offspring << " rabbits..." << endl ;
Rabbit* temp_rabbit ;
for(int i=0; i < offspring; i++){
int sex = random() % 2 + 1 ;
temp_rabbit = new Rabbit() ;
temp_rabbit->setSexe(sex);
rabbits.push_back(temp_rabbit);
cout << "one rabbit has been born." << endl ;
}
}
//makes rabbits date each other
void match_rabbits(vector<Rabbit*> & rabbits){
cout << "matching rabbits..." << endl ;
for(int r = 0; r < rabbits.size() ; r++ ){
Rabbit* first_rabbit_p = rabbits.front();
Rabbit* nth_rabbit_p = rabbits.at(r);
cout << "pointer to first rabbit: "<< first_rabbit_p << endl ;
cout << "pointer to rabbit n° " << r << ": " << nth_rabbit_p << "( " << sizeof( *nth_rabbit_p ) << "B )" << endl ;
cout << "sex parameter of dereferenced rabbit: " << rabbit.sex << endl ;
/*
if( rabbit.sex == Rabbit::FEMALE && rabbit.has_mated == false){
cout << "found a female" << endl ;
rabbit.match(rabbits) ;
} */
}
}
void pop_rabbits(vector<Rabbit*> & rabbits, int n){
vector<Rabbit*>::iterator rabbits_iterator ;
for(int r = 0 ; r < rabbits.size() ; r++ ){
Rabbit* rabbit = rabbits.back();
delete rabbit ;
rabbits.pop_back();
}
}
int main( int argc , const char* argv[] ){
srand(time(NULL));
vector<Rabbit*> rabbits = vector<Rabbit*>(0) ;
Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE) ;
Rabbit* eve ;
eve = new Rabbit() ;
eve->setSexe(Rabbit::FEMALE) ;
char * input;
input = new char[2] ;
try{
//populate with 2 rabbits.
rabbits.push_back(adam);
rabbits.push_back(eve);
delete adam ;
delete eve ;
do {
//memory_usage = getrusage(RUSAGE_SELF, struct rusage *usage);
if(rabbits.size() < 2){
break ;
}
cout << rabbits.size() << " rabbits ( " << "K )" << endl ;
cout << "Shoot some rabbits ? (Y/N) :" << endl ;
delete[] input ;
input = new char[2] ;
cin.getline(input,2);
if( strcmp(input,"Y") == 0 || strcmp(input,"y") == 0){
cout << "How many ? :" << endl ;
delete[] input ;
input = new char[16] ;
cin.getline(input,16);
pop_rabbits(rabbits, atoi(input));
continue ;
}
cout << "Continue ? (Y/Q) :" << endl ;
delete[] input ;
input = new char[2] ;
cin.getline(input,2);
if(strcmp(input,"Y") == 0 || strcmp(input,"y") == 0){
match_rabbits(rabbits);//let the rabbits date
}
if(strcmp(input,"Q") == 0 || strcmp(input,"q") == 0){
break ;
}
} while( true );
exit(0);
} catch ( exception& e ){
cout << e.what() << endl ; //print error
exit(1);
}
}
Here
Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ;
you've got a dangling pointer inside the vector
. vector
only deep copies the object of type used as vector
parameter - in your case it's Rabbit*
, not Rabbit
. So only pointers are copied, not objects.
Later you retrieve and use that dangling pointer and that invokes undefined behavior.
It seems that you are trying to do Java in C++.
Your problem is located here :
Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ;
new Rabbit
, allocates enough memory to store your Rabbit, calls Rabbit's constructor and returns a pointer containing the address where your Rabbit is stored (let's say it's 0x42424242).
Then you copy this address into the vector, which contains now one pointer (i.e. address) : 0x42424242.
When you call delete adam
, delete will call Rabbit's destructor for the instance stored at the given address, and then mark the region previously occupied by our Rabbit as free. Now the memory region at 0x42424242 does not stores a Rabbit
any more.
You keep this address in your vector, still thinking there's a Rabbit
there, but the place where it points is now invalid. It's called a dangling pointer
If you try to use the pointer in your vector, you might (or might not) get errors, depending on what is now contained at memory location 0x42424242. In theory, anything could happen.
What will trigger an error at each time is an attempt to call delete
on any pointer in the vector. Since the memory location is already marked as freed by the system, the error will be detected and your program will be stopped immediatly.
Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ; //i think we dont need the pointer anymore as we copied it to the vector
Here's you have mistake. Look:
adam = new Rabbit();
You get some piece of memory for your object, and get pointer to it's begin.
rabbits.push_back(adam);
You add into vector just variable with begin of allocated memory! You don't allocate new & copy. Because of it, after
delete adam ; //i think we dont need the pointer anymore as we copied it to the vector
it free memory, allocated in first string. But in vector pointer doesn't change, because it's just variable. So you mustn't free memory here, just when you need to delete rabbit.
Some advices: 1)You create enum sexes, so why variable sex is int? Better if it will be:
sexes sex;
2)Don't use pointers(if it's not some test project), use boost::shared_ptr, boost::scoped_ptr. It's more safety.
精彩评论