Can Anyone halp me fix this obscure error?

Hi I’m building agame engine out of the book SDL Game Programming. I’m adding polymorphism to the engine in the chapter I’m doing at the moment but when I compile with $ g++ main.cpp -std=c++98 -fpermissive -Wall -lSDL2 -lSDL2_image

I get the error:

/tmp/ccGeKxTV.o: In function Enemy::Enemy()': main.cpp:(.text._ZN5EnemyC2Ev[_ZN5EnemyC5Ev]+0x1b): undefined reference tovtable for Enemy’
collect2: error: ld returned 1 exit status

can anyone tell me the cause of this obscure error and how to fix it?

Thanks.

Enemy.h

#ifndef __Enemy__
#define __Enemy__

#include "GameObject.h"

class Enemy : public GameObject
{
public:
void load(int x, int y, int width, int height, std::string textureID);
void draw(SDL_Renderer* pRenderer);
void update();
void clean();
};

#endif // defined __Enemy_h__

Enemy.cpp

#include "Enemy.h"

void Enemy::update()
{
m_y += 1;
m_x += 1;
m_currentFrame = int((SDL_GetTicks() / 100) % 6);
}

Game.h

#ifndef __Game__
#define __Game__

#include"GameObject.h"

class Game
{
public:
Game() {}
~Game() {}



bool init(const char* title, int xpos, int ypos, int width,
int height, bool fullscreen);
void render();
void update();
void handleEvents();
void clean();
void draw();

// a function to access the private running variable
bool running() {return m_bRunning;}

private:
std::vector<GameObject*> m_GameObjects;
GameObject* m_player;
GameObject* m_enemy1;
GameObject* m_enemy2;
GameObject* m_enemy3;
SDL_Window * m_pWindow;
SDL_Renderer * m_pRenderer;
SDL_Surface * pTempSurface;

int m_currentFrame;

bool m_bRunning;
};

GameObject * m_go;
GameObject * m_player;
#endif /* defined(__Game_h__) */

Game.cpp

#include "Game.h"
#include "Enemy.h"
#include "GameObject.h"
#include "Game.h"

bool Game::init(const char* title, int xpos, int ypos, int width,
int height, bool fullscreen)
{
	m_player = new Player();
	m_enemy1 = new Enemy();
	m_enemy2 = new Enemy();
	m_enemy3 = new Enemy();

	m_GameObjects.push_back(m_player);
	m_GameObjects.push_back(m_enemy1);
	m_GameObjects.push_back(m_enemy2);
	m_GameObjects.push_back(m_enemy3);

	int flags = 0;

	if (fullscreen)
	{
	flags = SDL_WINDOW_FULLSCREEN;
	}

		// attempt to initialize SDL
		if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
		{
		std::cout<<"SDL Init Success\n";
		// init the window
		m_pWindow = SDL_CreateWindow(title, xpos, 
		ypos,width,height,flags);			       
		}
		else
		{
		std::cout<<"Window Init Fail\n";
		return false; //window init fail
		}

		if(m_pWindow != 0) //window init success
		{
		std::cout<<"Window Creation Success\n";
		m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);

			if(m_pRenderer !=0) // render init success
			{
			std::cout<<"Renderer Creation Success\n";
			SDL_SetRenderDrawColor(m_pRenderer, 255,0,0,255);
			}	
			else
			{
			std::cout<<"Renderer Init Fail\n";
			return false; // renderer init fail
			}



	}

	else
	{
	std::cout<<"SDL Init Fail\n";
	return false; // SDL init fail
	}

	
	
		//to load					
		if(!TheTextureManager::Instance()->load("assets/animate-alpha.png","animate", m_pRenderer))
		{
		return false;
		}

m_go = new GameObject();
m_player = new Player();

m_go->load(100,100,128,82, "animate");
m_player->load(300,300,128,82, "animate");
	
m_GameObjects.push_back(m_go);
m_GameObjects.push_back(m_player);

GameObject* m_enemy;

m_enemy = new Enemy();
m_enemy ->load(0,0,128,82,"animate");
m_GameObjects.push_back(m_enemy);

	std::cout<<"Init success\n";
	m_bRunning = true; // everything inited successfully
	// start the main loop

	return true;

}

void Game::render()
{

SDL_RenderClear(m_pRenderer);

// loop through our objects and draw them

for(std::vector<GameObject*>::size_type i =0; i!=m_GameObjects.size(); i++)
{
m_GameObjects[i]->draw(m_pRenderer);
}

//to draw
//TheTextureManager::Instance()->draw("animate",0,0, 128, 82, m_pRenderer);

//to animate
//TheTextureManager::Instance()->drawFrame("animate",100,100,128,82,1,m_currentFrame, m_pRenderer);

//m_go.draw(m_pRenderer);

//m_player.draw(m_pRenderer);

SDL_RenderPresent(m_pRenderer);

}

void Game::update()
{
//m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
//m_go.update();
//m_player.update();

for(std::vector<GameObject*>::size_type i = 0; i != m_GameObjects.size(); i++)
{
m_GameObjects[i]->update();
}

}

void Game::clean()
{
std::cout<<"Cleaning game\n";
SDL_DestroyWindow(m_pWindow);
SDL_DestroyRenderer(m_pRenderer);
SDL_Quit();
}




void Game::handleEvents()
{
SDL_Event event;
	if(SDL_PollEvent(&event))
	{
		switch (event.type)
		{
		case SDL_QUIT:
 		m_bRunning = false;
		break;
		}
	}
}

void Game::draw()
{
for(std::vector<GameObject*>::size_type i = 0; i != m_GameObjects.size(); i++)
{
m_GameObjects[i]->draw(m_pRenderer);
}
}

GameObject.h

#ifndef __GameObject__
#define __GameObject__

//#include <SDL_image.h>
#include "TextureManager.h"

class GameObject
{
public:
virtual void load(int x, int y, int width, int height, std::string textureID);
virtual void draw(SDL_Renderer* pRenderer);
virtual void update();
virtual void clean();


protected:

std::string m_textureID;
int m_currentFrame;
int m_currentRow;
int m_x;
int m_y;
int width;
int height;
int m_width;
int m_height;
};

#endif /* defined(__GameObject_h__) */

GameObject.cpp

#include "GameObject.h"

void GameObject::load(int x, int y, int width, int height, std::string textureID)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
m_textureID = textureID;

m_currentRow = 1;
m_currentFrame = 1;
}

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() 
{
    // Do Something
}

main.cpp

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include "TextureManager.h"
#include "TextureManager.cpp"
#include "GameObject.h"
#include "GameObject.cpp"
#include "Player.h"
#include "Player.cpp"
#include "Game.h"
#include "Game.cpp"
#include "Enemy.h"
#include "Enemy.cpp"

int main(int argc, char * args[])
{
Game * g_game = new Game;

g_game->init("Chapter 1", 100,100,640,480,false);

while(g_game->running())
{
	g_game->handleEvents();
	g_game->update();
	g_game->render();
	
	SDL_Delay(10);

}
g_game->clean();

return 0;

}

Player.h

#ifndef __Player__
#define __Player__

#include "GameObject.h"

class Player : public GameObject
{
public:
void load(int x, int y, int width, int height, std::string textureID);

void draw(SDL_Renderer* pRenderer);

void update();

void clean()
{
GameObject::clean();
std::cout<<"clean object\n";
}

};

#endif /*defined Player.h */

Player.cpp

#include "Player.h"

void Player::load(int x, int y, int width, int height, std::string textureID)
{
GameObject::load(x,y, width, height, textureID);
}

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

void Player::update()
{
m_x -=1;
}

TextureManager.h

#ifndef __TextureManager__
#define __TextureManager__

class TextureManager
{
public:
TextureManager(){}

bool load(std::string fileName, std::string id, SDL_Renderer* pRenderer);

//draw
void draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);

// drawframe
void drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);

std::map<std::string, SDL_Texture*> m_textureMap;

static TextureManager *s_pInstance;

static TextureManager* Instance()
{
	if(s_pInstance == 0)
	{
	s_pInstance = new TextureManager();
	return s_pInstance;
	}

return s_pInstance;
}

private:
~TextureManager(){}

};

typedef TextureManager TheTextureManager;

#endif /* defined(__TextureManager_h__) */

TextureManager.cpp

#include "TextureManager.h"

TextureManager* TextureManager::s_pInstance = 0;

bool TextureManager::load(std::string fileName, std::string id, SDL_Renderer* pRenderer)
{
SDL_Surface* pTempSurface = IMG_Load(fileName.c_str());

if(pTempSurface == 0)
{
return false;
printf("IMG_Load Error %s\n", SDL_GetError());
}
else
{
std::cout <<"IMG_Load Success!\n";
}

SDL_Texture* pTexture =
SDL_CreateTextureFromSurface(pRenderer, pTempSurface);

SDL_FreeSurface(pTempSurface);

// everything went ok, add texture to our list
if(pTexture !=0)
{
m_textureMap[id] = pTexture;
return true;
}

// reaching here means something went wrong
return false;
}

void TextureManager::draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;

srcRect.x =0;
srcRect.y =0;
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x =x;
destRect.y =y;

SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, &destRect, 0, 0, flip);
}

void TextureManager::drawFrame(std::string id, int x, int y, int width, int height, int currentRow,
int currentFrame, SDL_Renderer *pRenderer, SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;
srcRect.x = width * currentFrame;
srcRect.y = height * (currentRow -1);
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x = x;
destRect.y = y;

SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, &destRect, 0,0, flip);
}

I will have a guess at this.
Looks like your adding the enemy to the gameobject list and calling draw on all the objects.
Your enemy class does not contain the draw definition in the .cpp file.

    GameObject* m_enemy;
    m_enemy = new Enemy();
    m_enemy ->load(0,0,128,82,"animate");
    m_GameObjects.push_back(m_enemy);
    ...
    ...
}


void Game::render() {
    // loop through our objects and draw them
    for(std::vector<GameObject*>::size_type i =0; i!=m_GameObjects.size(); i++) {
    m_GameObjects[i]->draw(m_pRenderer);
}

Add to Enemy.cpp

void Enemy::draw(SDL_Renderer* pRenderer) {
    // Do something
}