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

Getting object properties from a pointer stored in a vector

本文关键字:指针 获取 取对象 属性 存储 向量      更新时间:2023-10-16

紧凑型描述

我很难找出问题所在,因为一些模糊的原因,我将指针存储在向量中的对象的属性似乎发生了变化。

详细说明

我有一个兔子类,它看起来像这样:

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<Rabbit*> 的指针矢量

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

我用它来存储指向新创建的兔子的指针。我将指向新创建的兔子的指针传递给这个向量,如下所示。

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

我的意图是,每当兔子像这样跳出来时,都能解放记忆。(我希望这是正确的方式(

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

此处为

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

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

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

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

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

它释放内存,在第一个字符串中分配。但在向量中,指针并没有改变,因为它只是可变的。所以你不能在这里释放内存,就在你需要删除rabbit的时候。

一些建议:1( 你创建了枚举性别,那么为什么可变性别是int呢?如果会更好:

sexes sex;

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