纯虚函数调用

发布于 2024-07-16 08:04:49 字数 17080 浏览 7 评论 0原文

我显然不“理解”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;
}

我已阅读 旧的新事物: 什么是 __purecall?Visual C++ 中 R6025 运行时错误的说明 但我不完全理解为什么上面的代码中会发生这种情况。

[编辑] 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; 
} 

I obviously do not 'grok' C++.

On this programming assignment, I have hit a dead end. A runtime error occurs at this line of code:

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

with the message "Runtime Error - pure virtual function call."

From my understanding, this error occurs if the function reference attempts to call the (virtual) base class while the child class is not currently instantiated. However, I do not see where I have made this mistake.

Relevant Code:
Professor's Code:

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

My Code:

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

I've read The Old New Thing: What is __purecall? and Description of the R6025 run-time error in Visual C++ but I do not fully understand why this is occurring in the above code.

[edit] Full listing of main.c (yes, all one file... part of the assignment requirements.)

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

明月松间行 2024-07-23 08:04:49

grid 的定义是什么以及如何填充它? 我敢打赌你是从 Animal 构造函数中完成的。 此时this的动态类型是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); 
}

请注意,您正在使用 this 参数调用 Savanna::setAt。 此时,this 的动态类型是 Animal,而不是 Wildebeest 或其他东西。 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 指针。

请注意更多事项。 当您构建 Wildebeest 列表时,会导致悬空指针:

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 的析构函数中,有一个匹配的删除调用,以便我们不会泄漏内存:

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 实例完全相同的问题出色地。

What is the definition of grid and how are you populating it? I bet you are doing it from the Animal constructor. At this time, the dynamic type of this is Animal, and not the type of the object that is eventually created.

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

Until the object is fully constructed, you cannot use the this pointer in a dynamic way, this includes calling virtual functions, or using the virtual function table.

To be more specific, you need to postpone using the this pointer, that is to say, storing it in the grid array, until after the object is fully constructed.

Here in the Animal constructor:

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

Note that you are calling Savanna::setAt with the this parameter. At this point, the dynamic type of this is Animal, not Wildebeest or some other thing. setAt does this:

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

The value of anim is the this pointer from the Animal constructor.

Note a few more things. When you are constructing the list of Wildebeest, you are causing a dangling pointer:

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

The WildeBeest named fred will go out of scope on the next line and be destroyed. You need to allocate it dynamically via 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;
}

In the destrcuctor of the Savanna, there is a matching call to delete so that we do not leak memory:

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

You will have exactly the same problem with the Lion instances as well.

月亮是我掰弯的 2024-07-23 08:04:49

你的问题之一是这些行……

   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++。

One of your problems is these lines ...

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

... create a Wildebeest on the stack, and in the constructor the address of that stack-dwelling Wildebeest is stuffed into w's grid, and then the Wildebeest goes out of scope.

Your Wildebeests and Lions need to live in the heap ...

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;

}

... and you need the comment because what you're doing is far, far away from idiomatic C++.

冰火雁神 2024-07-23 08:04:49

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

Well, I don't see the code that intializes the grid array. Perhaps grid it was created on the stack, or on the heap, and is therefore filled with uninitialized values. Uninitialized values could be anything, but they probably are not NULLs, and are definitely not valid pointers.

空城仅有旧梦在 2024-07-23 08:04:49

纯虚拟错误消息意味着被调用的函数没有实现; 它实际上调用了指向方法的指针类型的空指针。 (这就是语法为 =0; 的原因。)因此,无论发生什么情况,错误消息都会告诉您没有任何 Wildebeasts 被指向那里。

我很想添加一些代码来检查那里是否有任何东西。 您已经检查了 null,所以问题是那里是什么?

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

The pure virtual error message means that the function being called doesn't have an implementation; it's effectively invoking a null pointer of type pointer to method. (That's why the syntax is =0;.) So, whatever else is going on, the error message is telling you there aren't any Wildebeasts being pointed to there.

I'd be real tempted to add a little code to check whether there's ANYTHING there. You've got a check for null, so the question is what is there?

It's almost certain that array isn't being initialized the right way.

别在捏我脸啦 2024-07-23 08:04:49

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

我也愿意打赌,这可能与您构建野兽的方式有关,作为自动变量而不是动态分配它们。 一旦摆脱了这些假设,该内存就会被回收。

I think your problem is not a call to pure virtual function (compiler can never allow that so it doesn't really cause a runtime error), but that you are calling the function on an invalid memory area that therefore has an invalid virtual table.

I am also willing to bet that this may have something to do with how you construct your beasts, as automatic variables rather than dynamically allocating them. As soon as you get out of those ifs, that memory is recycled.

谢绝鈎搭 2024-07-23 08:04:49

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

Not necessarily the cause of your problem (I haven't read your code too deeply), but you are not using virtual destructors, which you should be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文