从存储在向量中的指针获取对象属性

时间:2011-07-07 13:03:21

标签: c++ oop pointers vector dynamic-memory-allocation

紧凑的描述

我有一个问题是找出什么是错的,由于一些模糊的原因,我将指针存储在矢量中的对象中的属性似乎被改变了。

详细说明

我有一个看起来像这样的兔子。

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

现在它是一个非常基本的类,析构函数仍然是空的,它有一些属性。 我还有一个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 个答案:

答案 0 :(得分:3)

下面

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

你在vector内有一个悬空指针。 vector仅深度复制用作vector参数的类型对象 - 在您的情况下为Rabbit*,而不是Rabbit。所以只复制指针,而不是对象。

稍后您将检索并使用该悬空指针,并调用未定义的行为。

答案 1 :(得分:1)

您似乎正在尝试使用C ++编写Java。

您的问题位于此处:

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。由于系统已将内存位置标记为已释放,因此将检测到错误并立即停止您的程序。

答案 2 :(得分: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

这是你的错误。 看:

adam = new Rabbit();

你为对象获得了一些内存,并获得了指向它的指针。

rabbits.push_back(adam);

你添加到vector只是变量与分配内存的开始!你没有分配新的&amp;复制。 因为它,在

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

它释放内存,在第一个字符串中分配。但是在向量指针中没有改变,因为它只是变量。 所以你不能在这里释放记忆,就像你需要删除兔子一样。

一些建议: 1)你创建了enum sexes,那么为什么变量性别是int?如果它会更好:

sexes sex;

2)不要使用指针(如果它不是某个测试项目),请使用boost :: shared_ptr,boost :: scoped_ptr。它更安全。