C++-虚拟继承

C++ - virtual inheritance

本文关键字:继承 虚拟 C++-      更新时间:2023-10-16

首先,我想说我最初是用C++初级读本学习C++的,但因为我想了解一些关于SDL库的知识,我决定从Shaun Mitchell那里获得"SDL游戏开发"一书。

现在是第3章,我在虚拟继承方面遇到了一些问题。我知道它是如何工作的,但在我的程序中找不到问题。

错误:

||=== Build: Debug in test (compiler: GNU GCC Compiler) ===|
C:UsersDocumentstest - Kopie (2)game.cpp||In member function 'void  Game::render()':|
C:UsersDocumentstest - Kopie (2)game.cpp|78|error: no matching function for call to 'GameObject::draw(SDL_Renderer*&)'|
C:UsersDocumentstest - Kopie (2)game.cpp|78|note: candidate is:|
C:UsersDocumentstest - Kopie (2)GameObject.h|11|note: virtual void  GameObject::draw()|
C:UsersDocumentstest - Kopie (2)GameObject.h|11|note:   candidate  expects 0 arguments, 1 provided|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

我知道我有太多的争论,但游戏对象有两个派生类,玩家类和敌人类。敌人类需要一个参数,但玩家类不需要。因此,当我删除参数时,生成器会给我更多的错误。

我会在这里列出所有代码:

game.h:

#include <SDL.h>
#include <SDL_image.h>
#include <iostream>
#include <vector>
#include "Player.h"
using namespace std;
class Game
{
public:
bool init(const char* title, int xpos, int ypos, int width, int height, int flags);
void render();
void update();
void handleEvents();
void clean();
bool running() { return m_bRunning; }
SDL_Renderer* getRenderer() const { return m_pRenderer; }
std::vector<GameObject*> m_gameObjects;
static Game* Instance()
{
if(s_pInstance == 0)
{
s_pInstance = new Game();
return s_pInstance;
}
return s_pInstance;
}
private:
Game() {}
bool m_bRunning;
SDL_Window* m_pWindow;
SDL_Renderer* m_pRenderer;
SDL_Texture* m_pTexture; // the new SDL_Texture variable
int m_currentFrame;
GameObject* m_go;
GameObject* m_player;
GameObject* m_enemy;
static Game* s_pInstance;
};   

game.cpp:

#include <SDL.h>
#include <iostream>
#include "game.h"
#include <SDL_image.h>
#include <vector>
using namespace std;
bool Game::init(const char* title, int xpos, int ypos, int width,
int height, int flags)
{
// attempt to initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
m_gameObjects.push_back(new Player(new LoaderParams(100, 100, 128, 82, "animate")));
m_gameObjects.push_back(new Enemy(new LoaderParams(300, 300, 128, 82, "animate")));
cout << "SDL init successn";
// init the window
m_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags);
if(m_pWindow != 0) // window init success
{
cout << "window creation successn";
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
if(m_pRenderer != 0) // renderer init success
{
cout << "renderer creation successn";
SDL_SetRenderDrawColor(m_pRenderer, 255,0,0,255);
if(!TextureManager::Instance()->load("explosion-sprite.png", "animate", m_pRenderer))
{
return false;
}
}
else
{
cout << "renderer init failn";
return false; // renderer init fail
}
}
else
{
cout << "window init failn";
return false; // window init fail
}
}
else
{
cout << "SDL init failn";
return false; // SDL init fail
}
cout << "init successn";
m_bRunning = true; // everything inited successfully,
return true;
}
void Game::render()
{
SDL_RenderClear(m_pRenderer); // clear the renderer to
for(std::vector<GameObject*>::size_type i = 0; i != m_gameObjects.size(); i++)
{
m_gameObjects[i]->draw(m_pRenderer);
}
SDL_RenderPresent(m_pRenderer); // draw to the screen
}
void Game::handleEvents(){
SDL_Event event;
if(SDL_PollEvent(&event)){
switch (event.type){
case SDL_QUIT:
m_bRunning = false;
break;
default:
break;
    }
}
}
void Game::clean(){
cout << "cleaning gamen";
SDL_DestroyWindow(m_pWindow);
SDL_DestroyRenderer(m_pRenderer);
SDL_Quit();
}
void Game::update()
{
for(std::vector<GameObject*>::size_type i = 0; i !=
m_gameObjects.size(); i++)
{
m_gameObjects[i]->update();
}
}
Game* Game::s_pInstance = 0;

TextureManager.h:

#include <SDL.h>
#include <SDL_image.h>
#include <iostream>
#include <map>
#include "Loaderparams.h"
using namespace std;
class TextureManager{
public:
bool load(string filename, string id, SDL_Renderer* Renderer);
void draw(string id, int x, int y, int width, int height, SDL_Renderer* Renderer, SDL_RendererFlip = SDL_FLIP_NONE);
void drawFrame(string id, int x, int y, int width, int height, int row, int frame, SDL_Renderer* Renderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
static TextureManager* Instance();
private:
TextureManager() {}
map<string, SDL_Texture*> textureMap;
};

TextureManager.cpp:

#include <SDL.h>
#include <iostream>
#include "TextureManager.h"
#include <SDL_image.h>
using namespace std;
bool TextureManager::load(string filename, string id, SDL_Renderer* Renderer){
SDL_Surface* surf = IMG_Load(filename.c_str());
if(surf == NULL)
return false;
SDL_Texture* text = SDL_CreateTextureFromSurface(Renderer, surf);
SDL_FreeSurface(surf);
if(text != 0){
textureMap[id] = text;
return true;
}
return false;
}

void TextureManager::draw(string id, int x, int y, int width, int height, SDL_Renderer* Renderer, SDL_RendererFlip flip){
SDL_Rect source;
SDL_Rect destination;
source.x = 0;
source.y = 0;
destination.w = source.w = width;
destination.h = source.h = height;
destination.x = x;
destination.y = y;
SDL_RenderCopyEx(Renderer, textureMap[id], &source, &destination, 0, 0, flip);
}

void TextureManager::drawFrame(string id, int x, int y, int width, int height, int row, int frame, SDL_Renderer* Renderer, SDL_RendererFlip flip){
SDL_Rect source;
SDL_Rect destination;
source.x = width * frame;
source.y = height * row;
destination.w = source.w = width;
destination.h = source.h = height;
destination.x = x;
destination.y = y;
SDL_RenderCopyEx(Renderer, textureMap[id], &source, &destination, 0, 0, flip);
}
TextureManager* s_pInstance = 0;
TextureManager* TextureManager::Instance(){
    if(s_pInstance == 0){
        s_pInstance = new TextureManager();
        return s_pInstance;
    }
    return s_pInstance;
}

player.h:

#include "GameObject.h"
using namespace std;
class Player : public SDLGameObject
{
public:
Player(const LoaderParams* pParams): SDLGameObject(pParams){}
virtual void draw();
virtual void update();
virtual void clean();
};
// Enemy class
class Enemy : public SDLGameObject
{
public:
Enemy(const LoaderParams* pParams);
virtual void draw();
virtual void update();
virtual void clean();
};

player.cpp:

#include "Player.h"
using namespace std;
void Player::load(int x, int y, int width, int height, string textureID)
{
GameObject::load(x, y, width, height, textureID);
}

void Player::draw()
{
SDLGameObject::draw(); // we now use SDLGameObject
}

void Player::update()
{
m_x -= 1;
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
}
void Player::clean() {}
//ENEMY
void Enemy::load(int x, int y, int width, int height, string
textureID)
{
GameObject::load(x, y, width, height, textureID);
}

void Enemy::draw(SDL_Renderer* pRenderer)
{
GameObject::draw(pRenderer);
}

void Enemy::update()
{
m_position.setX(m_position.getX() + 1);
m_position.setY(m_position.getY() + 1);
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
}
void Enemy::clean(){
}

Gameobject.h:

#include <iostream>
#include <SDL.h>
#include "TextureManager.h"
#include "Vector2D.h"
using namespace std;
class GameObject
{
public:
virtual void draw() = 0;
virtual void update() = 0;
virtual void clean() = 0;
protected:
GameObject(const LoaderParams* pParams) {}
virtual ~GameObject() {}
};

class SDLGameObject : public GameObject
{
public:
SDLGameObject(const LoaderParams* pParams) :
GameObject(pParams), m_position(pParams->getX(), pParams->getY())
{
m_width = pParams->getWidth();
m_height = pParams->getHeight();
m_textureID = pParams->getTextureID();
m_currentRow = 1;
m_currentFrame = 1;
}
virtual void draw();
virtual void update();
virtual void clean();
protected:
Vector2D m_position;
int m_width;
int m_height;
int m_currentRow;
int m_currentFrame;
std::string m_textureID;
};

Gameobject.cpp:

#include <iostream>
#include <SDL.h>
#include "GameObject.h"
using namespace std;
void GameObject::draw(SDL_Renderer* pRenderer)
{
TextureManager::Instance()->drawFrame(m_textureID, m_x, m_y,
m_width, m_height, m_currentRow, m_currentFrame, pRenderer);
}

void GameObject::update()
{
m_x += 1;
}
void GameObject::clean(){
}

//SDLGameObject
void SDLGameObject::draw()
{
TextureManager::Instance()->drawFrame(m_textureID,
(int)m_position.getX(), (int)m_position.getY(), m_width,
m_height, m_currentRow, m_currentFrame,
Game::Instance()->getRenderer());
}

还有一个Vector.h,但它对问题来说并不重要

class GameObject
{
    public:
    virtual void draw() = 0;
    virtual void update() = 0;
    virtual void clean() = 0;
    protected:
    GameObject(const LoaderParams* pParams) {}
    virtual ~GameObject() {}
};

只有draw()

所以你不能做m_gameObjects[i]->draw(m_pRenderer);

签名不匹配。GameObject类只有一个draw()

请记住,不能通过继承来更改虚拟函数的函数签名。签名必须保持不变。

我想您也应该在头文件中添加重载的方法签名。

virtual void draw();
virtual void draw(SDL_Renderer* pRenderer);

我没有检查整个代码,所以可能还有其他问题,但那个特定的错误消息表明具有该签名的draw方法不存在。

请记住,cpp文件将被编译成对象文件,而链接器是知道该方法存在的人,这就是为什么没有任何关于该重载方法的信息的编译器抱怨签名不合适的原因。

我现在也关注这本书。这是本书第3章的结尾,其中GameObject类被重构为抽象类,SDLGameObject被引入为具有SDL特定接口的子代。此外,Game类被重构为单例。出现此错误是因为在SDLGameObject及其子代(Player, Enemy)的新版本中,SDL renderer不再作为参数传递给它们的方法。相反,它是通过像Game::Instance()->getRenderer()这样的Game单例对象访问的。即使进行了所有更改,动画图像也拒绝显示在窗口中!直到我发现没有人调用TextureManager从文件中加载图像。我已经将新字段fileName及其相应的getter添加到类LoaderParams中,并将以下代码添加到SDLGameObject类的构造函数中:

TextureManager::Instance()->load(pParams->getFileName(), m_textureID, TheGame::Instance()->getRenderer());

那些关注这本书的人要注意!