2009-03-31 23 views
-1

私は明らかに 'grok' C++をしません。純粋な仮想関数呼び出し

このプログラミング割り当てでは、私は打ちのめされました。ランタイムエラーは、次のコード行で発生します。

「ランタイムエラー - 純粋仮想関数呼び出し」というメッセージが表示されます。

私の理解から、このエラーは、子クラスが現在インスタンス化されていない間にファンクション参照が(仮想)基本クラスを呼び出そうとした場合に発生します。しかし、私はこのミスをどこにしたのかわかりません。

関連するコード:
教授のコード:

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

私はThe Old New Thing: What is __purecall?Description of the R6025 run-time error in Visual C++を読んだが、これは上記のコードで発生している理由は、私は完全には理解していません。

[編集]の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; 
} 
+0

グリッドの定義はできますか? – Uri

答えて

10

gridの定義はどのようなものですか?どのように入力していますか?私はあなたがAnimalコンストラクタからやっていると確信しています。現時点では、この動的タイプはAnimalであり、最終的に作成されるオブジェクトのタイプではありません。

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

オブジェクトが完全に構築されるまでは、これは仮想関数を呼び出す、または仮想関数テーブルを使用することを含む、動的な方法でthisポインタを使用することはできません。

具体的には、thisポインタを使用して延期する必要があります。つまり、オブジェクトが完全に構築されるまでgrid配列に格納する必要があります。あなたはthisパラメータでSavanna::setAtを呼び出している

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はWildebeestやその他のものではなくAnimalです。関連項目SetAtは、この処理を行います。

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

animの値は、動物のコンストラクタからこのポインタです。

もう少し注意してください。あなたはヌーのリストを構築しているときは、ダングリングポインタを引き起こしている:

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は、次の行にスコープの外に行くと破壊されるという名前。あなたは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; 
} 

サバンナのdestrcuctorでは、我々はメモリリークが発生しないように削除するには、一致するコールがあります:

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

あなたは正確になりますライオンズの場合も同様の問題です。

+0

クラス定義にこのctorが含まれているので、あなたはそれを持っていると思います: Animal(Savanna *、int、int); 私は2つのintがグリッド座標であると確信しています。 –

+0

ええ、同じことが破壊にも適用されることに注意してください。 – jpalecek

+0

プログラムフローが次のように正確な問題ではないと私は信じていません。(1)grid [N] [N]をNsにすべて初期化すると、grid [] [] LionまたはWildebeest(3.)のgrid [i] [j] - > getType()(失敗した場所)の入力ごとに表示されます。 – locriani

1

まあ、私はグリッドアレイをintializesコードが表示されません。おそらくグリッドはスタック上またはヒープ上に作成されたため、初期化されていない値で埋められています。初期化されていない値は何でもかまいませんが、おそらくNULLではなく、間違いなく有効なポインタです。

+0

nullポインタを参照解除することで純粋なコールを得ることはできません –

+0

ポイントが少なすぎます。グリッドに何が入っているか分からなければ、私は(あるいは他の誰か)問題を診断できるとは思わない。 –

+0

私はそれを取り戻す。あなたの応答に私のコメントを参照してください:-) –

-1

必ずしもあなたの問題の原因ではありません(私はあなたのコードをあまりにも深く読んでいません)が、あなたは仮想デストラクタを使用していません。

0

純仮想エラーメッセージは、呼び出されている関数に実装がないことを意味します。メソッドへのポインタ型のヌルポインタを効果的に呼び出しています。 (そのため、構文は=0;です。)他に何が起こっていても、そこにはWildebeastが指摘されていないことを示すエラーメッセージが表示されます。

私はそこANYTHINGがありますかどうかを確認するために少しのコードを追加するために誘惑さ本当だろう。 nullのチェックがありますので、質問はです。

配列が正しい方法で初期化されていないことはほぼ確実です。

0

あなたの問題は純粋仮想関数(コンパイラは決して実行時エラーを引き起こさないようにすることはできません)を呼び出すのではないと思いますが、無効なメモリ領域に関数を呼び出しています無効な仮想テーブル。

これは、動的変数を割り当てるのではなく、自動変数としてあなたの獣を構築する方法と関係している可能性があります。あなたがそれらのifsから出るとすぐに、そのメモリはリサイクルされます。

3

あなたの問題の一つは、これらの行である...

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

...スタック上にヌーを作成し、コンストラクタでそのスタックに生息するヌーのアドレスは、ワットのグリッドに詰めていますその後、Wildebeestは範囲外になります。

あなたのヌーとライオンズがヒープに住んでする必要があります...

(もし!(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++からは遠く離れているので、コメントが必要です。

関連する問題