簡體   English   中英

純虛擬 Function 調用

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM