Получение свойств объекта из указателя, хранящегося в векторе - PullRequest
2 голосов
/ 07 июля 2011

Компактное описание

У меня проблема с выяснением, что не так, по какой-то непонятной причине свойства объекта, для которого я сохранил указатель в векторе, похоже, изменились.

Подробное объяснение

У меня есть класс Кролик, который выглядит следующим образом.

class Rabbit {
    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 );
}

Пока это базовый класс Verry, деструктор все еще пуст и имеет несколько свойств. У меня также есть указатель-указатель типа vector

vector<Rabbit*> rabbits = vector<Rabbit*>(0);

который я использую для хранения указателей на только что созданных кроликов. Я даю указатели вновь созданным кроликам на этот вектор вот так.

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

Мое намерение - освободить память всякий раз, когда кролика отрывают вот так. (Я надеюсь, что это правильный путь)

Rabbit* dead_rabbit = rabbits.back(); //obtain the pointer
delete dead_rabbit ; //free the associated memory
rabbits.pop_back(); //delete the pointer itself

Но у меня возникают проблемы, когда я пытаюсь получить доступ к свойству пола кролика, для которого указатель хранится в векторе

Rabbit* rabbit_p = rabbits.at(r) ;
cout << rabbit_p->sex << endl ; // prints a verry high number instead of 1 or 2

Итак, мой вопрос: почему это происходит, я неосознанно обращаюсь к другому месту в куче и зачитываю другое значение? а почему?

Ниже я приведу весь исходный код, он далек от правильного поведения кролика;) но я хотел проверить динамическое назначение памяти для объектов. Сначала вектор содержал только простых кроликов, но память не была освобождена, поэтому сейчас я тестирую подход с указателем.

Полный источник

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);
    }

}

Ответы [ 3 ]

3 голосов
/ 07 июля 2011

Здесь

Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ;

у вас есть висячий указатель внутри vector. vector только глубоко копирует объект типа, используемого как параметр vector - в вашем случае это Rabbit*, а не Rabbit. Таким образом, копируются только указатели, а не объекты.

Позже вы извлекаете и используете этот висячий указатель, который вызывает неопределенное поведение.

1 голос
/ 07 июля 2011

Похоже, вы пытаетесь сделать Java на C ++.

Ваша проблема находится здесь:

Rabbit* adam ;
adam = new Rabbit();
adam->setSexe(Rabbit::MALE);
rabbits.push_back(adam);
delete adam ; 

new Rabbit, выделяет достаточно памяти для хранения вашего Rabbit, вызывает конструктор Rabbit и возвращает указатель, содержащий адрес, где хранится ваш Rabbit (скажем, 0x42424242).

Затем вы копируете этот адрес в вектор, который теперь содержит один указатель (т.е. адрес): 0x42424242.

Когда вы вызываете delete adam, delete вызовет деструктор Rabbit для экземпляра, хранящегося по указанному адресу, а затем пометит область, ранее занятую нашим Rabbit, как свободную. Теперь область памяти по адресу 0x42424242 больше не хранит Rabbit.

Вы сохраняете этот адрес в своем векторе, все еще думая, что там есть Rabbit, но место, на которое он указывает, теперь недопустимо. Это называется свисающий указатель

Если вы попытаетесь использовать указатель в своем векторе, вы можете (или не можете) получать ошибки, в зависимости от того, что сейчас содержится в ячейке памяти 0x42424242. Теоретически все может произойти.

То, что будет вызывать ошибку каждый раз, это попытка вызвать delete для любого указателя в векторе. Поскольку ячейка памяти уже помечена как освобожденная системой, ошибка будет обнаружена, и ваша программа будет немедленно остановлена.

0 голосов
/ 07 июля 2011
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

Вот у вас ошибка. Смотри:

adam = new Rabbit();

Вы получаете часть памяти для вашего объекта и получаете указатель на его начало.

rabbits.push_back(adam);

Вы добавляете в вектор просто переменную с началом выделенной памяти! Вы не размещаете новые и не копируете. Из-за этого после

delete adam ; //i think we dont need the pointer anymore as we copied it to the vector

это свободная память, выделенная в первой строке. Но в векторе указатель не меняется, потому что он просто переменный. Поэтому вы не должны освобождать память здесь, только когда вам нужно удалить кролика.

Несколько советов: 1) Вы создаете enum sexs, так почему же переменный секс - это int? Лучше, если это будет:

sexes sex;

2) Не используйте указатели (если это не какой-то тестовый проект), используйте boost :: shared_ptr, boost :: scoped_ptr. Это больше безопасности.

...