【问题标题】:C++ - virtual inheritanceC++ - 虚拟继承
【发布时间】:2016-03-26 17:10:51
【问题描述】:

首先我想说的是,我最初是通过 C++ Primer 学习 C++,但因为我想了解一些有关 SDL 库的知识,所以我决定从 Shaun Mitchell 那里获得“SDL-Game-Development”一书。

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

错误:

||=== Build: Debug in test (compiler: GNU GCC Compiler) ===|
C:\Users\Documents\test - Kopie (2)\game.cpp||In member function 'void  Game::render()':|
C:\Users\Documents\test - Kopie (2)\game.cpp|78|error: no matching function for call to 'GameObject::draw(SDL_Renderer*&)'|
C:\Users\Documents\test - Kopie (2)\game.cpp|78|note: candidate is:|
C:\Users\Documents\test - Kopie (2)\GameObject.h|11|note: virtual void  GameObject::draw()|
C:\Users\Documents\test - 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)) ===|

我知道我的论点太多了,但 Gameobject 有 2 个派生类,玩家类和敌人类。敌人类需要一个参数,但玩家类不需要。因此,当我删除参数时,构建器会给我更多错误。

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

游戏.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;

};   

游戏.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 success\n";
// init the window
m_pWindow = SDL_CreateWindow(title, xpos, ypos, width, height, flags);
if(m_pWindow != 0) // window init success
{
cout << "window creation success\n";
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
if(m_pRenderer != 0) // renderer init success
{
cout << "renderer creation success\n";
SDL_SetRenderDrawColor(m_pRenderer, 255,0,0,255);

if(!TextureManager::Instance()->load("explosion-sprite.png", "animate", m_pRenderer))
{
return false;
}

}
else
{
cout << "renderer init fail\n";
return false; // renderer init fail
}
}
else
{
cout << "window init fail\n";
return false; // window init fail
}
}
else
{
cout << "SDL init fail\n";
return false; // SDL init fail
}
cout << "init success\n";
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 game\n";
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;

纹理管理器.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;
};

纹理管理器.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;
}

播放器.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();
};

播放器.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(){
}

游戏对象.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;
};

游戏对象.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,但它对问题并不重要

【问题讨论】:

  • 请缩进您的代码。没有人会阅读这个令人头疼的问题。
  • 我很确定可以删除 95% 的代码而不影响问题。这将使人们更有可能阅读它,甚至可能导致您自己发现问题所在。

标签: c++ inheritance sdl virtual abstract


【解决方案1】:

我猜你也应该在头文件中添加重载方法签名。

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

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

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

【讨论】:

    【解决方案2】:
    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]-&gt;draw(m_pRenderer);

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

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

    【讨论】:

      【解决方案3】:

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

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

      看书的人请注意!

      【讨论】:

        猜你喜欢
        • 2011-01-08
        • 1970-01-01
        • 2017-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多