简体   繁体   English

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

[英]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.我很难找出问题所在,由于某种晦涩的原因,我将指针存储在向量中的 object 中的属性似乎已更改。

Detailed explanation详细解释

I have a class Rabbit which looks like this:我有一只 class 兔子,它看起来像这样:

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

For now it's a very basic class, the destructor is still empty and it has a few properties.现在它是一个非常基本的 class,析构函数仍然是空的,它有一些属性。 I also have a pointer vector of type vector<Rabbit*>我还有一个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.我的意图是每当兔子像这样突然弹出时释放 memory。 ( 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.下面我将包含整个源代码,它远非正确的兔子面包行为,但我想测试对象的动态 memory 分配。 At first the vector contained just plain rabbits, but the memory wasn't released, so now I'm testing the pointer approach.起初向量只包含普通兔子,但 memory 没有发布,所以现在我正在测试指针方法。

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中有一个悬空指针。 vector only deep copies the object of type used as vector parameter - in your case it's Rabbit* , not Rabbit . vector仅深度复制用作vector参数的类型的 object - 在您的情况下,它是Rabbit* ,而不是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++.看来您正在尝试在 C++ 中执行 Java。

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). new Rabbit ,分配足够的 memory 来存储 Rabbit,调用 Rabbit 的构造函数并返回包含存储 Rabbit 的地址的指针(假设它是 0x42424242)。

Then you copy this address into the vector, which contains now one pointer (ie address): 0x42424242.然后将此地址复制到向量中,向量现在包含一个指针(即地址):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.当您调用delete adam时,delete 将为存储在给定地址的实例调用 Rabbit 的析构函数,然后将我们的 Rabbit 先前占用的区域标记为空闲。 Now the memory region at 0x42424242 does not stores a Rabbit any more.现在,位于 0x42424242 的 memory 区域不再存储Rabbit

You keep this address in your vector, still thinking there's a Rabbit there, but the place where it points is now invalid.你把这个地址保存在你的向量中,仍然认为那里有一只Rabbit ,但它指向的地方现在是无效的。 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.如果您尝试在向量中使用指针,您可能(或可能不会)收到错误,具体取决于 memory 位置 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.每次都会触发错误的是尝试在向量中的任何指针上调用delete Since the memory location is already marked as freed by the system, the error will be detected and your program will be stopped immediatly.由于 memory 位置已被系统标记为已释放,因此将检测到错误并立即停止您的程序。

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.你得到一些 memory 用于你的 object,并得到指向它开始的指针。

rabbits.push_back(adam);

You add into vector just variable with begin of allocated memory.您将分配的 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.它释放 memory,分配在第一个字符串中。 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.所以你不能在这里释放memory,只是当你需要删除兔子的时候。

Some advices: 1)You create enum sexes, so why variable sex is int?一些建议:1)你创建枚举性别,那么为什么变量性别是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. 2)不要使用指针(如果不是一些测试项目),使用 boost::shared_ptr, boost::scoped_ptr。 It's more safety.这样更安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM