2009-03-31 4 views
-1

Je ne fais évidemment pas de 'grok' C++.Appel de fonction virtuel pur

Sur cette affectation de programmation, j'ai atteint une impasse. Une erreur d'exécution se produit à cette ligne de code: «Erreur d'exécution - pur appel de fonction virtuelle »

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

avec le message D'après ce que je comprends, cette erreur se produit si la référence de la fonction tente d'appeler la classe de base (virtuelle) alors que la classe enfant n'est pas actuellement instanciée. Cependant, je ne vois pas où j'ai fait cette erreur.

code pertinent:
du Code du professeur:

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

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

J'ai lu The Old New Thing: What is __purecall? et Description of the R6025 run-time error in Visual C++ mais je ne comprends pas très bien pourquoi cela se produit dans le code ci-dessus .

[modifier] de la liste complète des main.c (oui, tout un fichier ... une partie des exigences d'affectation.)

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

Pouvez-vous fournir la définition de la grille? – Uri

Répondre

10

Quelle est la définition de grid et comment le remplissez-vous? Je parie que vous le faites du constructeur Animal. À ce moment, le type dynamique de ceci est Animal, et pas le type de l'objet qui est finalement créé.

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

Tant que l'objet est entièrement construit, vous ne pouvez pas utiliser le pointeur this d'une manière dynamique, cela inclut l'appel de fonctions virtuelles, ou en utilisant la table de fonction virtuelle. Pour être plus précis, vous devez reporter le pointeur this, c'est-à-dire le stocker dans le tableau grid, jusqu'à ce que l'objet soit entièrement construit.

Ici dans le constructeur d'animaux:

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

Notez que vous appelez Savanna::setAt avec le paramètre this. À ce stade, le type dynamique de this est Animal, pas gnou ou quelque chose d'autre. setAt fait ceci:

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

La valeur de anim est le ce pointeur du constructeur d'animaux.

Notez quelques autres choses.Lorsque vous construisez la liste des gnous, vous causez un pointeur ballants:

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

Le nom gnous fred se sont hors de portée sur la ligne suivante et être détruits. Vous devez allouer dynamiquement 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; 
} 

Dans le destrcuctor de la Savane, il y a un appel correspondant à supprimer afin que nous ne fuit pas la mémoire:

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

Vous aurez exactement le même problème avec les instances de Lion.

+0

Je pense que vous l'avez, puisque la définition de la classe contient ce ctor: Animal (Savanna *, int, int); Je parie que les deux ints sont les coordonnées de la grille. –

+0

Ouais, et notez qu'il en va de même pour la destruction. – jpalecek

+0

Je ne crois pas que ce soit le problème exact, car le flux du programme est le suivant: (1.) initialiser la grille [N] [N] tous Ns à NULL (2.) remplir les entrées aléatoires de la grille [] [] comme par entrée avec Lion ou Wildebeest (3.) grid [i] [j] -> getType() (où il échoue). – locriani

1

Eh bien, je ne vois pas le code qui intializes le tableau de grille . Peut-être que la grille a été créée sur la pile, ou sur le tas, et est donc remplie de valeurs non initialisées. Les valeurs non initialisées peuvent être n'importe quoi, mais elles ne sont probablement pas NULL et ne sont certainement pas des pointeurs valides.

+0

Vous n'obtiendrez pas un purecall de déréférencer un pointeur nul –

+0

Le point est, trop peu du code a été posté. Sans savoir ce qu'il y a dans la grille, je ne pense pas que je (ou quelqu'un d'autre) puisse diagnostiquer le problème. –

+0

Je le reprends; voir mon commentaire sur votre réponse :-) –

-1

Pas nécessairement la cause de votre problème (je n'ai pas lu votre code trop profondément), mais vous n'utilisez pas de destructeurs virtuels, ce que vous devriez être.

0

Le message d'erreur virtuel pur signifie que la fonction appelée n'a pas d'implémentation; il appelle effectivement un pointeur null de type pointer to method. (C'est pourquoi la syntaxe est =0;.) Donc, quoi qu'il se passe d'autre, le message d'erreur vous dit qu'il n'y a pas de Wildebeasts pointés là-bas.

Je serais réel tenté d'ajouter un peu de code pour vérifier s'il y a RIEN là. Vous avez un chèque pour null, alors la question est là?

Il est presque certain que le tableau n'est pas initialisé correctement.

0

Je pense que votre problème n'est pas un appel à la fonction virtuelle pure (le compilateur ne peut jamais le permettre, donc cela ne cause pas vraiment une erreur d'exécution), mais que vous appelez la fonction sur une zone mémoire invalide. table virtuelle invalide.

Je suis également prêt à parier que cela peut avoir quelque chose à voir avec la façon dont vous construisez vos bêtes, comme des variables automatiques plutôt que de les allouer dynamiquement. Dès que vous sortez de ces ifs, cette mémoire est recyclée.

3

L'un de vos problèmes est ces lignes ...

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

... créer un gnous sur la pile, et dans le constructeur de la adresse de cette gnous vivant pile est fourré dans la grille de w , et puis le gnou sort de la portée.

Votre gnous et les Lions ont besoin de vivre dans le tas ...

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; 

}

.. Et vous avez besoin du commentaire parce que ce que vous faites est loin, loin de C++ idiomatique.