纯虚函数调用

时间:2009-03-31 23:51:18

标签: c++ runtime pure-virtual

我显然不会'克服'C ++。

在这个编程任务中,我已经走到了尽头。此行代码发生运行时错误:

else if (grid[i][j]->getType() == WILDEBEEST) { ...

显示消息“运行时错误 - 纯虚函数调用。”

根据我的理解,如果函数引用在当前未实例化子类时尝试调用(虚拟)基类,则会发生此错误。但是,我没有看到我犯了这个错误。

相关守则:
教授的代码:

const int LION = 1; 
const int WILDEBEEST = 2;

//
// .
// .
// .
//

class Animal {
    friend class Savanna;   // Allow savanna to affect animal 
public: 
    Animal(); 
    Animal(Savanna *, int, int); 
    ~Animal(); 
    virtual void breed() = 0;    // Breeding implementation 
    virtual void move() = 0;     // Move the animal, with appropriate behavior 
    virtual int getType() = 0;   // Return if wildebeest or lion 
    virtual bool starve() = 0;   // Determine if animal starves 
protected: 
    int x,y;        // Position in the savanna, using the XY coordinate plane
    bool moved;     // Bool to indicate if moved this turn 
    int breedTicks; // Number of ticks since breeding 
    Savanna *savanna; 
};

//
// .
// .
// .
//

void Savanna::Display() 
{ 
 int i,j; 

 cout << endl << endl; 
 for (j=0; j<SAVANNASIZE; j++) 
 { 
  for (i=0; i<SAVANNASIZE; i++) 
  { 
   if (grid[i][j]==NULL){ 
    setrgb(0);
    cout << " "; 
   }
   else if (grid[i][j]->getType()==WILDEBEEST) // RUNTIME ERROR HERE
   {
     setrgb(7);
     cout << "W"; 
   }
   else {
        setrgb(3);
        cout << "L";
   }
  } 

     cout << endl; 
 } 
 setrgb(0);
} 

我的代码:

class Wildebeest: public Animal {

friend class Savanna;    // Allow the Savanna to affect the animal, as per spec
public: 
    Wildebeest(); 
    Wildebeest(Savanna *, int, int);    // accepts (pointer to a Savanna instance, X Position, Y Position)
    void breed();           // Perform breeding, and check breedTick 
    void move();            // move the animal.
    int getType();              // returns WILDEBEEST
    bool starve();                  // if starving, returns 0. (counterintuitive, I know.)
};

int Wildebeest::getType() {

    return WILDEBEEST;
}

我已阅读The Old New Thing: What is __purecall?Description of the R6025 run-time error in Visual C++,但我不完全理解上述代码中出现这种情况的原因。

[edit] main.c的完整列表(是的,所有文件......分配要求的一部分。)

// 
//  This program simulates a 2D world with predators and prey. 
//  The predators (lions) and prey (wildebeest) inherit from the 
//  Animal class that keeps track of basic information about each 
//  animal (time ticks since last bred, position on the savanna). 
// 
//  The 2D world is implemented as a separate class, Savanna, 
//  that contains a 2D array of pointers to type Animal. 
// 

// **************************************************************** 

#include <iostream> 
#include <string> 
#include <vector> 
#include <cstdlib> 
#include <time.h> 
#include "graphics.h"

using namespace std; 

int wrapTo20 (int value) {

    if (0 > value) {

        value = 19;
    } else if (20 == value) {

        value = 0;
    }

    return value;
}

const int SAVANNASIZE = 20; 
const int INITIALBEEST = 100; 
const int INITIALLIONS = 5; 
const int LION = 1; 
const int WILDEBEEST = 2; 
const int BEESTBREED = 3;
const int LIONBREED = 8; 
const int LIONSTARVE = 3; 

// Forward declaration of Animal classes so we can reference it 
// in the Savanna class 
class Animal; 
class Lion; 
class Wildebeest; 

// ========================================== 
// The Savana class stores data about the savanna by creating a 
// SAVANNASIZE by SAVANNASIZE array of type Animal. 
// NULL indicates an empty spot, otherwise a valid object 
// indicates an wildebeest or lion.  To determine which, 
// invoke the virtual function getType of Animal that should return 
// WILDEBEEST if the class is of type Wildebeest, and Lion otherwise. 
// ========================================== 

class Savanna 
{ 
friend class Animal;   // Allow Animal to access grid 
friend class Lion;   // Allow Animal to access grid 
friend class Wildebeest;   // Allow Animal to access grid 
public: 
 Savanna(); 
 ~Savanna(); 
 Animal* getAt(int, int); 
  void setAt(int, int, Animal *); 
 void Display(); 
 void SimulateOneStep(); 
private: 
 Animal* grid[SAVANNASIZE][SAVANNASIZE]; 
}; 


// ========================================== 
// Definition for the Animal base class. 
// Each animal has a reference back to 
// the Savanna object so it can move itself 
// about in the savanna. 
// ========================================== 
class Animal 
{ 
friend class Savanna;   // Allow savanna to affect animal 
public: 
 Animal(); 
 Animal(Savanna *, int, int); 
 ~Animal(); 
 virtual void breed() = 0; // Whether or not to breed 
 virtual void move() = 0; // Rules to move the animal 
 virtual int getType() = 0; // Return if wildebeest or lion 
 virtual bool starve() = 0; // Determine if animal starves 
protected: 
 int x,y;   // Position in the savanna 
 bool moved;   // Bool to indicate if moved this turn 
 int breedTicks;   // Number of ticks since breeding 
 Savanna *savanna; 
}; 

// ====================== 
// Savanna constructor, destructor 
// These classes initialize the array and 
// releases any classes created when destroyed. 
// ====================== 
Savanna::Savanna() 
{ 
 // Initialize savanna to empty spaces 
 int i,j; 
 for (i=0; i<SAVANNASIZE; i++) 
 { 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   grid[i][j]=NULL; 
  } 
 } 
} 

Savanna::~Savanna() 
{ 
 // Release any allocated memory 
 int i,j; 
 for (i=0; i<SAVANNASIZE; i++) 
 { 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   if (grid[i][j]!=NULL) delete (grid[i][j]); 
  } 
 } 
} 

// ====================== 
// getAt 
// Returns the entry stored in the grid array at x,y 
// ====================== 
Animal* Savanna::getAt(int x, int y) 
{ 
 if ((x>=0) && (x<SAVANNASIZE) && (y>=0) && (y<SAVANNASIZE)) 
  return grid[x][y]; 
 return NULL; 
} 

// ====================== 
// setAt 
// Sets the entry at x,y to the 
// value passed in.  Assumes that 
// someone else is keeping track of 
// references in case we overwrite something 
// that is not NULL (so we don't have a memory leak) 
// ====================== 
void Savanna::setAt(int x, int y, Animal *anim) 
{ 
 if ((x>=0) && (x<SAVANNASIZE) && (y>=0) && (y<SAVANNASIZE)) 
 { 
  grid[x][y] = anim; 
 } 
} 

// ====================== 
// Display 
// Displays the savanna in ASCII.  Uses W for wildebeest, L for lion. 
// ====================== 
void Savanna::Display() 
{ 
 int i,j; 

 cout << endl << endl; 
 for (j=0; j<SAVANNASIZE; j++) 
 { 
  for (i=0; i<SAVANNASIZE; i++) 
  { 
   if (grid[i][j]==NULL){ 
    setrgb(0);
    cout << " "; 
   }
   else if (grid[i][j]->getType()==WILDEBEEST) 
   {
     setrgb(7);
     cout << "W"; 
   }
   else {
        setrgb(3);
        cout << "L";
   }
  } 

     cout << endl; 
 } 
 setrgb(0);
} 

// ====================== 
// SimulateOneStep 
// This is the main routine that simulates one turn in the savanna. 
// First, a flag for each animal is used to indicate if it has moved. 
// This is because we iterate through the grid starting from the top 
// looking for an animal to move . If one moves down, we don't want 
// to move it again when we reach it. 
// First move lions, then wildebeest, and if they are still alive then 
// we breed them. 
// ====================== 
void Savanna::SimulateOneStep() 
{ 
 int i,j; 
 // First reset all animals to not moved 
 for (i=0; i<SAVANNASIZE; i++) 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   if (grid[i][j]!=NULL) grid[i][j]->moved = false; 
  } 
 // Loop through cells in order and move if it's a Lion 
 for (i=0; i<SAVANNASIZE; i++) 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   if ((grid[i][j]!=NULL) && (grid[i][j]->getType()==LION)) 
   { 
   if (grid[i][j]->moved == false) 
   { 
    grid[i][j]->moved = true; // Mark as moved 
    grid[i][j]->move(); 
   } 
   } 
  } 
 // Loop through cells in order and move if it's an Wildebeest 
 for (i=0; i<SAVANNASIZE; i++) 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   if ((grid[i][j]!=NULL) && (grid[i][j]->getType()==WILDEBEEST)) 
   { 
    if (grid[i][j]->moved == false) 
    { 
     grid[i][j]->moved = true; // Mark as moved 
     grid[i][j]->move(); 
    } 
   } 
  } 
 // Loop through cells in order and check if we should breed 
 for (i=0; i<SAVANNASIZE; i++) 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
        // Kill off any lions that haven't eaten recently 
   if ((grid[i][j]!=NULL) && 
       (grid[i][j]->getType()==LION)) 
   { 
    if (grid[i][j]->starve()) 
    { 
     delete (grid[i][j]); 
     grid[i][j] = NULL; 
    } 
   } 
  } 
 // Loop through cells in order and check if we should breed 
 for (i=0; i<SAVANNASIZE; i++) 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
   // Only breed animals that have moved, since 
   // breeding places new animals on the map we 
   // don't want to try and breed those 
   if ((grid[i][j]!=NULL) && (grid[i][j]->moved==true)) 
   { 
    grid[i][j]->breed(); 
   } 
  } 
} 

// ====================== 
// Animal Constructor 
// Sets a reference back to the Savanna object. 
// ====================== 
Animal::Animal() 
{ 
 savanna = NULL; 
 moved = false; 
 breedTicks = 0; 
 x=0; 
 y=0; 
} 

Animal::Animal(Savanna *savana, int x, int y) 
{ 
 this->savanna = savana; 
 moved = false; 
 breedTicks = 0; 
 this->x=x; 
 this->y=y; 
 savanna->setAt(x,y,this); 
} 

// ====================== 
// Animal destructor 
// No need to delete the savanna reference, 
// it will be destroyed elsewhere. 
// ====================== 
Animal::~Animal() 
{ } 

// Start with the Wildebeest class and its required declarations 
class Wildebeest: public Animal {

    friend class Savanna;   // Allow savanna to affect animal 
public: 
    Wildebeest(); 
    Wildebeest(Savanna *, int, int); 
 void breed();          // Whether or not to breed 
 void move();           // Rules to move the animal 
 int getType();         // Return if wildebeest or lion 
 bool starve(); 
};

bool Wildebeest::starve() {

    return 1;
}


// ====================== 
// Wildebeest constructors 
// ====================== 

Wildebeest::Wildebeest() {

}

Wildebeest::Wildebeest(Savanna * sav, int x, int y) : Animal(sav, x, y) {

}

// ====================== 
// Wldebeest Move 
// Look for an empty cell up, right, left, or down and 
// try to move there. 
// ====================== 

void Wildebeest::move() {

    int loc1, loc2, loc3, loc4;
    int x1, x2, x3, x4;
    int y1, y2, y3, y4;

    x1 = wrapTo20(x);
    y1 = wrapTo20(y + 1);

    x2 = wrapTo20(x + 1);
    y2 = wrapTo20(y);

    x3 = wrapTo20(x);
    y3 = wrapTo20(y - 1);

    x4 = wrapTo20(x - 1);
    y4 = wrapTo20(y);

    loc1 = savanna->getAt(x1, y1)->getType();
    loc2 = (int)savanna->getAt(x2, y2)->getType();
    loc3 = savanna->getAt(x3, y3)->getType();
    loc4 = savanna->getAt(x4, y4)->getType();

    while (!moved) {
        int x = 1 + (rand() % 4);
        switch (x) {

            case 1:
                if (!loc1) savanna->setAt(x1, y1, this);
                break;

            case 2:
                if (!loc2) savanna->setAt(x2, y2, this);
                break;

            case 3:
                if (!loc3) savanna->setAt(x3, y3, this);
                break;

            case 4:
                if (!loc4) savanna->setAt(x4, y4, this);
                break;

            default:
                break;
        }
    }
}



// ====================== 
// Wildebeest getType 
// This virtual function is used so we can determine 
// what type of animal we are dealing with. 
// ====================== 
int Wildebeest::getType() {

    return WILDEBEEST;
}

// ====================== 
// Wildebeest breed 
// Increment the tick count for breeding. 
// If it equals our threshold, then clone this wildebeest either 
// above, right, left, or below the current one. 
// ====================== 

void Wildebeest::breed() {
    breedTicks++;

    if (2 == breedTicks) {
        breedTicks = 0;
    }

}

// ***************************************************** 
// Now define Lion Class and its required declarations
// ***************************************************** 

class Lion: public Animal {

    friend class Savanna;   // Allow savanna to affect animal 
public: 
    Lion(); 
    Lion(Savanna *, int, int); 
 void breed();          // Whether or not to breed 
 void move();           // Rules to move the animal 
 int getType();         // Return if wildebeest or lion 
 bool starve(); 
};


// ====================== 
// Lion constructors 
// ====================== 

Lion::Lion() {

}

Lion::Lion(Savanna * sav, int x, int y) : Animal(sav, x, y) {

}


// ====================== 
// Lion move 
// Look up, down, left or right for a lion.  If one is found, move there 
// and eat it, resetting the starveTicks counter. 
// ====================== 

void Lion::move() {

    int loc1, loc2, loc3, loc4;
    int x1, x2, x3, x4;
    int y1, y2, y3, y4;

    x1 = wrapTo20(x);
    y1 = wrapTo20(y + 1);

    x2 = wrapTo20(x + 1);
    y2 = wrapTo20(y);

    x3 = wrapTo20(x);
    y3 = wrapTo20(y - 1);

    x4 = wrapTo20(x - 1);
    y4 = wrapTo20(y);

    loc1 = savanna->getAt(x1, y1)->getType();
    loc2 = (int)savanna->getAt(x2, y2)->getType();
    loc3 = savanna->getAt(x3, y3)->getType();
    loc4 = savanna->getAt(x4, y4)->getType();

    while (!moved) {
        int x = 1 + (rand() % 4);
        switch (x) {

            case 1:
                if (!loc1) savanna->setAt(x1, y1, this);
                break;

            case 2:
                if (!loc2) savanna->setAt(x2, y2, this);
                break;

            case 3:
                if (!loc3) savanna->setAt(x3, y3, this);
                break;

            case 4:
                if (!loc4) savanna->setAt(x4, y4, this);
                break;

            default:
                break;
        }
    }
}

// ====================== 
// Lion getType 
// This virtual function is used so we can determine 
// what type of animal we are dealing with. 
// ====================== 

int Lion::getType() {

    return LION;
}

// ====================== 
// Lion breed 
// Creates a new lion adjacent to the current cell 
// if the breedTicks meets the threshold. 
// ====================== 

void Lion::breed() {

    breedTicks++;

    if (2 == breedTicks) {
        breedTicks = 0;
    }

}


// ====================== 
// Lion starve 
// Returns true or false if a lion should die off 
// because it hasn't eaten enough food. 
// ====================== 


bool Lion::starve() {

    return 1;
}


// ====================== 
//     main function 
// ====================== 
int main() 
{ 
  string s; 
  srand((int)time(NULL));  // Seed random number generator 
  Savanna w; 
  int initialWildebeest=0;
  int initialLions=0;

  // enter initial number of wildebeest
  int beestcount = 0; 
  while(initialWildebeest <= 0 || initialWildebeest > INITIALBEEST){
    cout << "Enter number of initial Wildebeest (greater than 0 and less than " << INITIALBEEST << ") : ";
    cin >> initialWildebeest;
  }
  // Randomly create wildebeests and place them in a randomly choosen empty spot in savanna 


    int i;
    bool placed = 0;

    for ( i = 0; i < initialWildebeest; i++) {
        while (!placed) {
            int x = 1 + (rand() % 20);
            int y = 1 + (rand() % 20);

            if (!(w.getAt(x, y))){
                Wildebeest fred(&w, x, y);
                placed = 1;
            }
        }
        placed = 0;
    }


  // Enter initial number of lions 
  int lioncount = 0; 
  while(initialLions <= 0 || initialLions > INITIALLIONS){
    cout << "Enter number of initial Lions (greater than 0 and less than " << INITIALLIONS << ") : ";
    cin >> initialLions;
  }
  // Randomly create lions and place them in a randomly choosen empty spot in savanna

  placed = 0;

  for ( i = 0; i < initialLions; i++) {
        while (!placed) {
            int x = 1 + (rand() % 20);
            int y = 1 + (rand() % 20);

            if (!(w.getAt(x, y))){
                Lion ronald(&w, x, y);
                placed = 1;
            }
        }
        placed = 0;
    }

  // Run simulation forever, until user cancels 
  int count=0;
  while (true) 
  { 
  gotoxy(0,0);
  w.Display(); 
  w.SimulateOneStep();
  Sleep(500); 
  count++;
  if(count == 20){
   count=0;
   cout << endl << "Press enter for next step, ctrl-c to quit" << endl; 
   getline(cin,s);
   clearline(23); 

   }
  } 
  return 0; 
} 

6 个答案:

答案 0 :(得分:10)

grid的定义是什么?你如何填充它?我打赌你是从Animal构造函数中做到的。此时,动态类型为Animal,而不是最终创建的对象的类型。

Animal::Animal()
{
    grid[i][j] = this; // the type of this is Animal
}

在对象完全构造之前,不能以动态方式使用this指针,包括调用虚函数或使用虚函数表。

更具体地说,您需要使用this指针推迟,也就是说,将其存储在grid数组中,直到完全构造对象为止。

这里是Animal构造函数:

Animal::Animal(Savanna *savana, int x, int y) 
{ 
 this->savanna = savana; 
 moved = false; 
 breedTicks = 0; 
 this->x=x; 
 this->y=y; 
 savanna->setAt(x,y,this); 
}

请注意,您使用Savanna::setAt参数调用this。此时,动态类型this是动物,而不是牛羚或其他东西。 setAt这样做:

void Savanna::setAt(int x, int y, Animal *anim) 
{ 
 if ((x>=0) && (x<SAVANNASIZE) && (y>=0) && (y<SAVANNASIZE)) 
 { 
  grid[x][y] = anim; 
 } 
}

anim的值是来自Animal构造函数的this指针。

请注意更多内容。当你构建牛羚列表时,你会引起一个悬空指针:

for ( i = 0; i < initialWildebeest; i++) {
    while (!placed) {
            int x = 1 + (rand() % 20);
            int y = 1 + (rand() % 20);

            if (!(w.getAt(x, y))){
****                       Wildebeest fred(&w, x, y);
                    placed = 1;
            }
    }
    placed = 0;
}

名为fred的WildeBeest将超出下一行的范围并被销毁。您需要通过new动态分配它:

for ( i = 0; i < initialWildebeest; i++) {
    while (!placed) {
            int x = 1 + (rand() % 20);
            int y = 1 + (rand() % 20);

            if (!(w.getAt(x, y))){
                    Wildebeest *fred = new Wildebeest(&w, x, y);
                    placed = 1;
            }
    }
    placed = 0;
}

在Savanna的destrcuctor中,有一个匹配的删除调用,以便我们不会泄漏内存:

Savanna::~Savanna() 
{ 
 // Release any allocated memory 
 int i,j; 
 for (i=0; i<SAVANNASIZE; i++) 
 { 
  for (j=0; j<SAVANNASIZE; j++) 
  { 
****       if (grid[i][j]!=NULL) delete (grid[i][j]); 
  } 
 } 
}

您也会遇到与Lion实例完全相同的问题。

答案 1 :(得分:3)

你的一个问题是这些问题......

   if (!(w.getAt(x, y))){
      Wildebeest fred(&w, x, y);
      placed = 1;
   }

...在堆栈上创建一个牛羚,在构造函数中,该堆栈住宅牛羚的地址被塞入w的网格,然后牛羚超出范围。

你的牛羚和狮子需要活在堆里......

if(!(w.getAt(x,y))){

  // hey maintenance programmer, this looks like I'm leaking the Wildebeest,
  // but chillax, the Savannah is going to delete them 

  Wildebeest *fred = new Wildebeest(&w, x, y);
  placed = 1;

}

...你需要评论,因为你所做的事情远非惯用的C ++。

答案 2 :(得分:1)

好吧,我没有看到初始化网格数组的代码。也许网格是在堆栈上或堆上创建的,因此填充了未初始化的值。未初始化的值可以是任何值,但它们可能不是NULL,并且绝对不是有效的指针。

答案 3 :(得分:0)

纯虚拟错误消息表示被调用的函数没有实现;它有效地调用了一个指向类型指针的空指针。 (这就是为什么语法是=0;。)所以,无论发生什么,错误信息告诉你没有任何Wildebeasts指向那里。

真实想要添加一些代码来检查那里是否有 ANYTHING 。你有一个null的检查,所以问题是那里的什么?

几乎可以肯定,数组没有以正确的方式初始化。

答案 4 :(得分:0)

我认为你的问题不是对纯虚函数的调用(编译器永远不会允许它,所以它不会真正导致运行时错误),而是你在无效的内存区调用函数,因此它具有无效虚拟表。

我也愿意打赌,这可能与你如何构建你的野兽有关,作为自动变量而不是动态分配它们。一旦你摆脱了那些ifs,那个记忆就会被回收。

答案 5 :(得分:-1)

不一定是你问题的原因(我没有深入阅读你的代码),但是你没有使用虚拟析构函数,你应该这样做。