【问题标题】:Getting object properties from a pointer stored in a vector从存储在向量中的指针获取对象属性
【发布时间】:2011-09-30 11:04:36
【问题描述】:

简洁说明

我很难找出问题所在,由于某种晦涩的原因,我将指针存储在向量中的对象的属性似乎已更改。

详细解释

我有一个类 Rabbit,如下所示:

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&lt;Rabbit*&gt; 类型的指针向量

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

【问题讨论】:

  • 这种情况下的解决方法很简单:不要使用指针。没有理由这样做。

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


【解决方案1】:

这里

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

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

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

【讨论】:

  • 所以我不应该在那个指针上调用 delete,如果我理解它正确的 delete 没有引用计数,并且无论如何都会清除内存。还是指针没有被 push_back 复制?
  • @Dimitry:指针被复制,但这里指向的对象不是。 delete 没有引用计数。您的选择是:此时不使用delete,使用共享智能指针,或者只使用vector&lt;Rabbit&gt;
  • 有没有办法做一个智能垃圾(&ptr p)函数,对那个地址的数据进行指针计数,只有在没有指针时才删除?
  • 好的,稍后我会深入研究共享智能指针,现在我将跳过删除。现在有道理了。谢谢
  • @Dimitri :如果你想要引用计数,那么考虑boost::shared_ptr
【解决方案2】:
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。更安全。

【讨论】:

  • 这是一个测试项目,是的,我已经习惯了 java,在 c++ 中没有 gc,也没有 vm 所以我正在定位自己的基本内存管理。计划是在我掌握了基础知识后对其进行重新编码以提高指针。
  • @DaNooby :你能做的最好的事情就是停止使用new
  • 一直困扰我的,以及我理解 boost 步骤的地方是,通过使用这种技术,我创建的指针数量是我实际需要的两倍。指针,以及它在向量中的副本。至少如果我在我的 main() 中这样做,如果我理解正确的话,函数或方法中的本地指针应该死掉并迷路。
  • @ildjarn 我猜我对 Java 有一种“新”的痴迷 :) 好的,使用 boost。
  • @ildjarn new 是个好东西,但仅限于 shared_ptr a(new int); 这样的概念。 =)
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    • 2013-09-12
    • 2016-02-25
    • 1970-01-01
    相关资源
    最近更新 更多