[英]Pure Virtual Function Call
我顯然不'grok' C++。
在這個編程任務中,我遇到了死胡同。 這行代碼出現運行時錯誤:
else if (grid[i][j]->getType() == WILDEBEEST) { ...
帶有消息“運行時錯誤 - 純虛擬 function 調用。”
據我了解,如果 function 引用嘗試調用(虛擬)基礎 class 而子 class 當前未實例化,則會發生此錯誤。 但是,我看不出我在哪里犯了這個錯誤。
相關代碼:
教授密碼:
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;
}
grid
的定義是什么,你是如何填充它的? 我敢打賭你是從 Animal 構造函數做的。 此時 this 的動態類型是Animal
,而不是最終創建的 object 的類型。
Animal::Animal()
{
grid[i][j] = this; // the type of this is Animal
}
在完全構造 object 之前,您不能以動態方式使用this
指針,這包括調用虛函數,或使用虛擬 function 表。
更具體地說,你需要推遲使用this
指針,也就是將它存儲在grid
數組中,直到object完全構造之后。
在 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 將從下一行的 scope 中取出 go 並被摧毀。 您需要通過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 的析構函數中,有一個匹配的 delete 調用,這樣我們就不會泄漏 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]);
}
}
}
Lion 實例也會遇到完全相同的問題。
你的問題之一是這些線......
if (!(w.getAt(x, y))){
Wildebeest fred(&w, x, y);
placed = 1;
}
... 在堆棧上創建一個 Wildebeest,在構造函數中,該堆棧居住的 Wildebeest 的地址被填充到 w 的網格中,然后 Wildebeest 離開 scope。
你的角馬和獅子需要生活在堆里......
如果 (.(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++ 相去甚遠。
好吧,我沒有看到初始化網格數組的代碼。 也許 grid 它是在堆棧或堆上創建的,因此填充了未初始化的值。 未初始化的值可以是任何值,但它們可能不是 NULL,並且絕對不是有效的指針。
純虛擬錯誤消息意味着被調用的 function 沒有實現; 它有效地調用了指向方法的類型指針的 null 指針。 (這就是為什么語法是=0;
。)所以,無論發生什么其他事情,錯誤消息都會告訴您那里沒有任何 Wildebeasts 被指向。
我真的很想添加一些代碼來檢查那里是否有任何東西。 您已經檢查了 null,所以問題是那里有什么?
幾乎可以肯定數組沒有以正確的方式初始化。
我認為您的問題不是對純虛擬 function 的調用(編譯器永遠不允許這樣做,因此它不會真正導致運行時錯誤),而是您在無效的虛擬 ZCD69B4957F016CD818D7BF3D6 區域上調用 function。
我也願意打賭,這可能與你如何構建你的野獸有關,作為自動變量而不是動態分配它們。 一旦你擺脫了這些如果,memory 就會被回收。
不一定是您的問題的原因(我沒有深入閱讀您的代碼),但您沒有使用虛擬析構函數,您應該使用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.