虚拟关键字似乎被忽略了

Virtual keyword seems to be ignored

本文关键字:被忽略了 关键字 虚拟      更新时间:2023-10-16

这是我的第一个"大"C++项目,我被卡住了。我正在尝试创建一个简单的ASCII roguelike。我有一个由Player类和Monster类继承的角色类。怪物职业由吸血鬼和狼人职业继承。

在GameSystem类的startGame函数中,我将Monsters数组的每个元素(应该填充有吸血鬼和狼人对象)提交给moveAround函数。吸血鬼、狼人和怪物都有这个功能,但只有怪物移动功能可以访问。如果你在下面的代码中注意到,我在Monster类中提供了虚拟关键字。

这让我觉得我在用子类填充Monster数组时搞砸了。我在GameSystem构造函数中通过随机确定怪物阵列中的特定元素是狼人还是吸血鬼来做到这一点。

我使用的是代码块,在编译方面,我有C++跟随C++11。

GameSystem.cpp

#include <iostream>
#include <string>
#include "GameSystem.h"
#include "Map.h"
#include "Player.h"
#include "Werewolf.h"
#include "Vampire.h"
#include "conio.h"
#include <cstdio>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <time.h>
GameSystem::GameSystem(string mapName){
    srand (time(NULL));
    _map.load(mapName);
    _map.obtainSpawningLocations(_player.getToken(),_player);
    Werewolf werewolf;
    Vampire vampire;
for(int i = 0; i < 5; i++){
    _spawnValue = rand() % 2;   //We generate either 1 or 2
    if(_spawnValue==1){
        _theMonsters[i] = werewolf;
        cout<<"Werewolf"<<endl;
    }
    else{
        _theMonsters[i] = vampire;
        cout<<"Vampire"<<endl;
    }
    _map.obtainSpawningLocations(_theMonsters[i].getToken(),_theMonsters[i]);
}
}
void GameSystem::startGame(){
bool isOver = false;
    while(isOver!=true){
        _map.print();
        movePlayer();
        for(int i = 0; i <5; i++){
        _theMonsters[i].moveAround(); //prints out Monster.moveAround()
        //I need it to print out Vampire.moveAround() and //Werewolf.moveAround
        }
       }
}
void GameSystem::movePlayer(){
    char input;
    input = getch();
    string clearScreenString(100,'n'); //Prints out a newLine char 100 times
    cout << clearScreenString;
    _map.checkMovement(input, _player);
}

char GameSystem::getch(){
    char buf=0;
    struct termios old={0};
    fflush(stdout);
    if(tcgetattr(0, &old)<0)
        {perror("tcsetattr()");}
    old.c_lflag&=~ICANON;
    old.c_lflag&=~ECHO;
    old.c_cc[VMIN]=1;
    old.c_cc[VTIME]=0;
    if(tcsetattr(0, TCSANOW, &old)<0)
        {perror("tcsetattr ICANON");}
    if(read(0,&buf,1)<0)
        {perror("read()");}
    old.c_lflag|=ICANON;
    old.c_lflag|=ECHO;
    if(tcsetattr(0, TCSADRAIN, &old)<0)
        {perror ("tcsetattr ~ICANON");}
    //printf("%cn",buf);
    return buf;
 }

GameSystem.h

#pragma once
#include "Map.h"
#include <string>
#include <list>
using namespace std;
class GameSystem
{
public:
    GameSystem(string mapName);       //Constructor
    void startGame();    //Start the game
    char getch();
    void movePlayer();
private:
    //int _numberOfMonsters = 5;    //We'll make this a random number later
    Map _map;
    Player _player;
    Monster _monster;
    Monster _theMonsters[5];
    int _x;
    int _y;
    int _spawnValue;
};

特征.cpp

#include <string>
#include "Character.h"
using namespace std;

Character::Character(){
}

char Character::getToken(){
return _token;
}
void Character::setLocation(int x, int y){
_x = x;
_y = y;
}
void Character::getLocation(int &x, int &y){
x = _x;
y = _y;
}

字符.h

#pragma once
#include <string>
class Character{
public:
    Character();
    char getToken();
    void setLocation(int x, int y);
    void getLocation(int &x, int &y);
protected:
    int _x;
    int _y;
    char _token = '!';
};

Map.cpp

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <cstring>
#include <random>
#include <ctime>
#include <tuple>
#include "Map.h"
#include <time.h>
Map::Map(){
srand (time(NULL));
}
void Map::load(string levelName){
    ifstream theStream;
    theStream.open(levelName);
    if(theStream.fail()){
        perror(levelName.c_str());
        system("PAUSE");
        exit(1);
    }
    string line;
    while(getline(theStream, line)){
    _mapData.push_back(line);
    }
        theStream.close();
}
void Map::obtainSpawningLocations(char characterToken,Character &_character){
/*
Below code provides all the possible spawning locations for the player
and stores them in an array of tuples.
*/
tuple<int,int> myTuple[600];    //Hard coded 600 value is messy.  Change later
int numberOfSpawnPoints = 0;
int upperLimitForNumberGenerator = 0;
/*
The for loop below records all of the possible spawning locations and stores them in the tuple array
*/
    for(int i = 0; i<_mapData.size();i++){
        for(int j = 0; j<_mapData[i].size();j++){
            if(_mapData[i][j]=='.'){
                    get<0>(myTuple[numberOfSpawnPoints]) = j;
                    get<1>(myTuple[numberOfSpawnPoints]) = i;
                    numberOfSpawnPoints++;
                    }
                }
    }
upperLimitForNumberGenerator = numberOfSpawnPoints;
int characterCoordinates = rand()%upperLimitForNumberGenerator;
int xCoordinate = get<0>(myTuple[characterCoordinates]);
int yCoordinate = get<1>(myTuple[characterCoordinates]);
_mapData[yCoordinate][xCoordinate] = characterToken;   //Remember y is first and x is second
_character.setLocation(xCoordinate, yCoordinate);
}
void Map::print(){
    for(int i=0;i<_mapData.size(); i++){
        printf("%sn", _mapData[i].c_str());
    }
    printf("n");
}
void Map::checkMovement(char input, Player &aPlayer){ 
int x;
int y;
aPlayer.getLocation(x,y);
char aLocation;
 switch(input) {
    case 'w':
    case 'W':   //If 1 up from the player token is a '.' then we move him up
                //via a different function
                //Otherwise we do nothing.
                aLocation = returnLocation(x,y-1);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y-1][x] = '@';
                aPlayer.setLocation(x,y-1);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;


    case 'a':
    case 'A':
                aLocation = returnLocation(x-1,y);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y][x-1] = '@';
                aPlayer.setLocation(x-1,y);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;
    case 's':
    case 'S':
                aLocation = returnLocation(x,y+1);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y+1][x] = '@';
                aPlayer.setLocation(x,y+1);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;
    case 'd':
    case 'D':
                aLocation = returnLocation(x+1,y);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y][x+1] = '@';
                aPlayer.setLocation(x+1,y);
                }
                else
                cout<<"Can't go here!"<<endl;



        break;
    default:
        cout<<"Invalid input";
        system("PAUSE");
        break;
    }
}
char Map::returnLocation(int x, int y){
cout<<x<<endl;
cout<<y<<endl;
char aSpot = _mapData[y][x];
return aSpot;
}

Map.h

#pragma once
#include <vector>
#include <fstream>
#include <string>
#include <tuple>
#include <ctime>
#include <random>
#include "Player.h"
#include "Monster.h"
using namespace std;
class Map
{
public:
    Map();      //Constructor
    void load(string levelName);
    void obtainSpawningLocations(char characterToken, Character &aCharacter);
    void checkMovementMonsters(char input, Monster &aMonster);
    //void obtainSpawningLocationsForMonsters(char characterToken, Monster aMonster);
    void print();
    void checkMovement(char input, Player &aPlayer);
    void movement(char characterToken);
    char returnLocation(int x,int y);
   // int numberOfSpawnPoints;
private:
    vector <string> _mapData;
    Player _player;
    Monster _monster;

};

Monster.cpp

#include <iostream>
#include <string>
#include "Monster.h"
using namespace std;
Monster::Monster(){
}
void Monster::moveAround(){
cout<<"Monster Mash"<<endl;
}

怪物.h

#pragma once
#include <string>
#include "Character.h"
class Monster: public Character{
public:
    Monster();
    virtual void moveAround();

protected:
    char _token = 'M';
    int _x;
    int _y;
};

狼人.cpp

#include <iostream>
#include <string>
#include "Werewolf.h"
using namespace std;
Werewolf::Werewolf(){
}
void Werewolf::moveAround(){
    cout<<"Werewolf moving around"<<endl;
}

狼人.h

#pragma once
#include <string>
#include "Character.h"      //For inheritance/polymorphism
#include "Monster.h"

class Werewolf: public Monster{
public:
    Werewolf();
    void moveAround();
private:
    char _token = 'W';
};

吸血鬼.cpp

#include <iostream>
#include <string>
#include "Vampire.h"
using namespace std;
Vampire::Vampire(){
}
void Vampire::moveAround(){
    cout<<"Vampire moving around"<<endl;
}

吸血鬼.h

#pragma once
#include <string>
#include "Character.h"      //For inheritance/polymorphism
#include "Monster.h"

class Vampire: public Monster{
public:
    Vampire();
    virtual void moveAround();
private:
    char _token = 'V';
};

播放器.cpp

Player::Player(){
}

Player.h

#pragma once
#include <string>
#include "Character.h"
using namespace std;
class Player : public Character {
public:
    Player();  
protected:
    int _x;
    int _y;
    char _token = '@';
};

#include <iostream>
#include "GameSystem.h"
using namespace std;
int main()
{
GameSystem gameSystem("LevelOne.txt");
gameSystem.startGame();
    return 0;
}

级别文本文件:

############################################
#..........................................#
#..........................................#
#...........................^..............#
#..........................................#
#......................#...................#
#......................#...................#
#......................#...................#
#............^.........#...................#
#......######..........#..&................#
#.....................#...................#
#......................#...................#
#..........................................#
#..........................................#
############################################

_theMonsters[i].moveAround()上没有发生虚拟调度。您需要有一个指向monster类型对象的指针数组。事实上,在尝试设置类层次结构的方式中存在相当多的问题,比如在派生类中具有相同名称的成员变量。

这里有几个问题。

  1. Monster::_tokenWerewolf::_tokenVampire::_token都是不同的变量。Werewolf对象同时具有Monster::_tokenWerewolf::_token
  2. Monster _theMonsters[5]是一个由5个Monster对象组成的数组。它永远不会是VampireWerewolf,永远是Monster。由于对象总是相同的类型:Monster,因此不需要进行虚拟调度

虚拟调度仅适用于指针和引用。要实现这一点,您需要使用指向Monsters:Monster* _theMonsters[5]的指针数组。然后你可以像_theMonsters[i] = new Vampire()一样填充它。如果你这样做,你需要记住在处理完对象时delete,或者你可以使用像std::unique_ptrstd::shared_ptr这样的智能指针来代替原始的Monster*