Click to See Complete Forum and Search --> : Problems with Absract Classes


BossOfTheGame
February 7th, 2008, 11:11 AM
I have a two classes one is abstract and another is inherited

// this is part of CGUI.h
class CGUI
{
public:
//other stuff
virtual void Init() = 0;
virtual void Paint() = 0;
};



//this is part of LoadScreen.h

#include "CGUI.h"

class LoadScreen : public CGUI
{
public:
virtual void Init();
virtual void Paint();
}


now in my definition of those classes I get problems


//this is part of LoadScreen.cpp
#include "LoadScreen.h"

void LoadScreen::Paint()
{
//stuff
}

void LoadScreen::Init()
{
//stuff
}



when I do this I get 2 errors

error LNK2001: unresolved external symbol "public virtual void __thiscall CGUI::Paint(void)" (?Paint@CGUI@@UAEXXZ)
error LNK2001: unresolved external symbol "public virtual void __thiscall CGUI::Init(void)" (?Init@CGUI@@UAEXXZ)
fatal error LNK1120: 2 unresolved externals

TheCPUWizard
February 7th, 2008, 11:17 AM
Based on the limited information you posted (which is obviously not enough to reproduce the problem), everything looks good. What Compiler (including version) are you using???

mczapar
February 7th, 2008, 11:32 AM
Not entirely certain how important it is, since it has been a while for abstract classes for me, but I do notice that you don't have a semicolon on the end of your Loadscreen.h

BossOfTheGame
February 7th, 2008, 12:38 PM
I'll post the whole header file if that makes a difference. I'm using Visual Studio 2005.


#ifndef LOADSCREEN_H
#define LOADSCREEN_H

#include "CGUI.h"

class CGUI;

class LoadScreen: public CGUI
{
public:
virtual void Init();
virtual void Paint();
///constructor
LoadScreen();
//destructor
~LoadScreen();
};

#endif LOADSCREEN_H



#ifndef CGUI_H
#define CGUI_H

#include "CommonInclude.h"
#include "EventReciver.h"
#define DO_NOT_EXIT -1
#define WINDOW_HEIGHT 640
#define WINDOW_WIDTH 480

class EventReciver;

class CGUI
{
public:
//variables
IrrlichtDevice* m_Device;
EventReciver* m_EventReciver;
s32 m_ExitCode;
//functions
virtual void Init() = 0;
virtual void Paint() = 0;
void Exit(s32 exitCode);
//constructor
CGUI();
//destructor
~CGUI();
};
#endif CGUI_H




//this is LoadScreen.cpp
#include "LoadScreen.h"

LoadScreen::~LoadScreen()
{
}
void onPlay(SEvent event, CGUI* source)
{
//stuff

}
void onOption(SEvent event, CGUI* source)
{
//stuff
}
void onHelp(SEvent event, CGUI* source)
{
//stuff
}
void onExit(SEvent event, CGUI* source)
{
//stuff
}
LoadScreen::LoadScreen()
{
}
void LoadScreen::Paint()
{
//stuff
}
void LoadScreen::Init()
{
// stuff
}



I can always post more code if necessay, but I don't think you all want to spend all day reading through everything. I thought I had everything necessary in the first post. But it is odd because I am using the open source Irrlicht Graphics Engine and it defines a class IEventReciver which is abstract and I overwrite it with EventReciver, but that doesn't seem to cause a problem when I implement its pure virtual functions.

Plasmator
February 7th, 2008, 12:53 PM
I just skimmed through your code, but I noticed that you never defined (or at least, never showed us that you did) CGUI's constructor and destructor.

laserlight
February 7th, 2008, 12:58 PM
I suggest that you simplify to:
#ifndef LOADSCREEN_H
#define LOADSCREEN_H

#include "CGUI.h"

class LoadScreen : public CGUI
{
public:
virtual void Init();
virtual void Paint();
///constructor
LoadScreen();
//destructor
~LoadScreen();
};

#endif

#ifndef CGUI_H
#define CGUI_H

class CGUI
{
public:
virtual void Init() = 0;
virtual void Paint() = 0;

//destructor
virtual ~CGUI() {}
};

#endif

//this is LoadScreen.cpp
#include "LoadScreen.h"

LoadScreen::~LoadScreen()
{
}

LoadScreen::LoadScreen()
{
}

void LoadScreen::Paint()
{
}

void LoadScreen::Init()
{
}

Then test with:
#include <memory>
#include "CGUI.h"
#include "LoadScreen.h"

int main()
{
LoadScreen screen;
screen.Paint();
screen.Init();

std::auto_ptr<CGUI> gui(new LoadScreen);
gui->Paint();
gui->Init();
}

Note that CGUI's destructor should be declared virtual since it is a base class.

BossOfTheGame
February 7th, 2008, 11:43 PM
I got the link errors to go away by adding a body for the two virtual functions. Now I don't want to use the Paint and Init functions I defined right there. I want to use the init and paint functions I define in the derived class. When I take away those lines I get the linker errors

#include "CGUI.h"

CGUI::~CGUI()
{
this->Exit(0);
}

CGUI::CGUI()
{
m_ExitCode = DO_NOT_EXIT;
m_EventReciver = new EventReciver();
m_Device = createDevice(video::EDT_OPENGL,core::dimension2d<s32>(WINDOW_HEIGHT,WINDOW_WIDTH),16,false,true,true,m_EventReciver);
if(m_Device == 0)
{
printf("\nFatal Error: Device could not be created\n");
this->Exit(1);
}
else{

video::IVideoDriver* driver = m_Device->getVideoDriver();
gui::IGUIEnvironment* env = m_Device->getGUIEnvironment();
Init();
while(m_Device->run() && driver)
{
if(m_ExitCode != DO_NOT_EXIT)
{
break;
}
else if (m_Device->isWindowActive())
{
driver->beginScene(true, true,video::SColor(255,255,255,255));
Paint();
driver->endScene();
}
}
}
}
void CGUI::Exit(s32 exitCode)
{
m_ExitCode = exitCode;
}
void CGUI::Paint(){}
void CGUI::Init(){}

laserlight
February 7th, 2008, 11:52 PM
I got the link errors to go away by adding a body for the two virtual functions.
Have you tried out my minimal example? I did not need to turn the pure virtual functions into ordinary virtual functions, yet I did not get any linker errors when I tested.

BossOfTheGame
February 8th, 2008, 12:05 AM
I did try your test, however it did not work, but I just figured out why. In the constructor for CGUI I made a call to Init and Paint, however they were defined as pure virtual functions so it gave me a linker error. How can I call those functions and just get whatever the base class defines them as?

Paul McKenzie
February 8th, 2008, 12:12 AM
I did try your test, however it did not work, So the test that was posted as-is by laserlight didn't work, or it did work?
but I just figured out why. In the constructor for CGUI I made a call to Init and PaintIf they are unimplemented, you can't call them without a linker error.

Regards,

Paul McKenzie

laserlight
February 8th, 2008, 12:14 AM
In the constructor for CGUI I made a call to Init and Paint, however they were defined as pure virtual functions so it gave me a linker error. How can I call those functions and just get whatever the base class defines them as?
Do not call virtual functions (pure or otherwise) in constructors. They will probably not do what you expect.

Read: When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked? (http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5)

EDIT:
Oh wait, you wanted to "just get whatever the base class defines them as". In that case obviously they cannot be pure virtual, and you will get what you expect when you call them in the base class constructor.