纯虚函数调用
我显然不“理解”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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
grid
的定义是什么以及如何填充它? 我敢打赌你是从 Animal 构造函数中完成的。 此时this的动态类型是Animal
,而不是最终创建的对象的类型。在对象完全构造之前,您不能以动态方式使用 this 指针,这包括调用虚函数或使用虚函数表。
更具体地说,您需要推迟使用
this
指针,即将其存储在grid
数组中,直到对象完全构造完成。在 Animal 构造函数中:
请注意,您正在使用
this
参数调用Savanna::setAt
。 此时,this
的动态类型是 Animal,而不是 Wildebeest 或其他东西。 setAt 的作用是:anim 的值是来自 Animal 构造函数的 this 指针。
请注意更多事项。 当您构建 Wildebeest 列表时,会导致悬空指针:
名为
fred
的 WildeBeest 将超出下一行的范围并被销毁。 您需要通过 new 动态分配它:在 Savanna 的析构函数中,有一个匹配的删除调用,以便我们不会泄漏内存:
您将遇到与 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 isAnimal
, and not the type of the object that is eventually created.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 thegrid
array, until after the object is fully constructed.Here in the Animal constructor:
Note that you are calling
Savanna::setAt
with thethis
parameter. At this point, the dynamic type ofthis
is Animal, not Wildebeest or some other thing. setAt does this: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:
The WildeBeest named
fred
will go out of scope on the next line and be destroyed. You need to allocate it dynamically vianew
:In the destrcuctor of the Savanna, there is a matching call to delete so that we do not leak memory:
You will have exactly the same problem with the Lion instances as well.
你的问题之一是这些行……
在堆栈上创建一个角马,并在构造函数中将该堆栈中的角马的地址填充到w的网格中,然后角马就去超出范围。
你的牛羚和狮子需要生活在堆中...
if (!(w.getAt(x, y))){
}
...并且你需要注释,因为你所做的与惯用语相去甚远C++。
One of your problems is these lines ...
... 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))){
}
... and you need the comment because what you're doing is far, far away from idiomatic C++.
好吧,我没有看到初始化网格数组的代码。 也许网格是在堆栈或堆上创建的,因此填充了未初始化的值。 未初始化的值可以是任何值,但它们可能不是 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.
纯虚拟错误消息意味着被调用的函数没有实现; 它实际上调用了指向方法的指针类型的空指针。 (这就是语法为
=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.
我认为您的问题不是对纯虚拟函数的调用(编译器永远不会允许这样做,因此它不会真正导致运行时错误),而是您在无效的内存区域上调用该函数,因此具有无效的虚拟表。
我也愿意打赌,这可能与您构建野兽的方式有关,作为自动变量而不是动态分配它们。 一旦摆脱了这些假设,该内存就会被回收。
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.
不一定是您问题的原因(我没有太深入地阅读您的代码),但您没有使用虚拟析构函数,而您应该使用虚拟析构函数。
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.