Sign in to follow this  
sheep19

simple OOP Linker error help

Recommended Posts

Hello. It's been a while since I even coded because school exams are approaching, so I decided to make a quick revision today. I grabbed my book and wrote a simple program. I decided to break the code in files myself. So, I have done it, but I'm missing something because I get a nasty liker error (my worst). So here's my code: Classes.h
#ifndef CLASSES_H
#define CLASSES_H

class Critter
{
public:
	Critter( const std::string& name = "" );
	std::string GetName() const;

private:
	std::string m_Name;
};

class Farm
{
public:
	Farm( int spaces = 1 );
	void Add( const Critter& aCritter );
	void RollCall() const;

private:
	std::vector< Critter > m_Critters;
};

#endif




main
#include <iostream>
#include <string>
#include <vector>

#include "Classes.h"

int main()
{
	Critter crit( "Poochie" );
	std::cout << "My critter's name is " << crit.GetName() << std::endl;

	std::cout << "\nCreating critter farm.\n";
	Farm myFarm(3);

	std::cout << "\nAdding three critters to the farm.\n";
	myFarm.Add( Critter("Moe") );
	myFarm.Add( Critter("Larry") );
	myFarm.Add( Critter("Curly") );

	std::cout << "\nCalling Roll...\n";
	myFarm.RollCall();

	system("PAUSE");
	return 0;
}




Classes.cpp
#include <iostream>
#include <string>
#include <vector>

#include "Classes.h"

Critter::Critter( const std::string& name ): m_Name( name )
{
}

inline std::string Critter::GetName() const
{
	return m_Name;
}

Farm::Farm( int spaces )
{
	m_Critters.reserve( spaces );
}

void Farm::RollCall() const
{
	for( std::vector< Critter >::const_iterator iter = m_Critters.begin(); iter < m_Critters.end(); ++iter )
		std::cout << iter->GetName() << " here.\n";
}

Edit: Oops, forgot the error. 1>Linking... 1>main.obj : error LNK2019: unresolved external symbol "public: void __thiscall Farm::Add(class Critter const &)" (?Add@Farm@@QAEXABVCritter@@@Z) referenced in function _main 1>C:\My Documents\Visual Studio 2005\Projects\Test2\Debug\Test2.exe : fatal error LNK1120: 1 unresolved externals [Edited by - sheep19 on May 8, 2008 8:36:20 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by gah_ribaldi
Shouldn't there be a classes.cpp file with the actual implememtation of the class methods?


Yes, there is, but I haven't included it. Maybe I should.

Share this post


Link to post
Share on other sites
Um, where's your Add( ... ) function for farm?

You declare it in classes.h, but in your classes.cpp there is no implementation. So of course, when you try to invoke that function in main, your link will fail.

Just looks like you forgot to write the actual function :)

Share this post


Link to post
Share on other sites
Well, that should be pretty straight forward. Just what the linker complains about: You didn't provide an implementation of the Farm::Add() method.

This is what's missing from your Classes.cpp file:
void Farm::Add(const Critter& aCritter) {
m_Critters.push_back(aCritter);
}


EDIT: Darn, beaten to it. Need... to... type... faster!

-Markus-

Share this post


Link to post
Share on other sites
I decided to split the classes in different files. I achieved this in two ways, but I'm not sure which one is the best.

1ST WAY:

Critter.h

#ifndef CRITTER_H
#define CRITTER_H

<b>class Farm;</b>
class Critter
{
public:
Critter( const std::string& name = "" );
std::string GetName() const;

private:
std::string m_Name;
};

#endif



Farm.h

#ifndef FARM_H
#define FARM_H

//#include "Critter.h" -> not using this
class Critter;
class Farm
{
public:
Farm( int spaces = 1 );
void Add( const Critter& aCritter );
void RollCall() const;

private:
std::vector< Critter > m_Critters;
};

#endif



Farm.cpp

#include <iostream>
#include <string>
#include <vector>

#include "Farm.h"
#include "Critter.h" // ->have to use this

Farm::Farm( int spaces )
{
m_Critters.reserve( spaces );
}

void Farm::Add( const Critter& aCritter )
{
m_Critters.push_back( aCritter );
}

void Farm::RollCall() const
{
for( std::vector< Critter >::const_iterator iter = m_Critters.begin(); iter < m_Critters.end(); ++iter )
std::cout << iter->GetName() << " here.\n";
}



So I'm avoiding cyclic dependency. I think it's unnecessary though because Critter doesn't need Farm.

--------------------------------------------------------------------------------

2nd way

Critter.h

#ifndef CRITTER_H
#define CRITTER_H

class Critter
{
public:
Critter( const std::string& name = "" );
std::string GetName() const;

private:
std::string m_Name;
};

#endif



Farm.h"

#ifndef FARM_H
#define FARM_H

#include "Critter.h"

class Farm
{
public:
Farm( int spaces = 1 );
void Add( const Critter& aCritter );
void RollCall() const;

private:
std::vector< Critter > m_Critters;
};

#endif



Farm.cpp

#include <iostream>
#include <string>
#include <vector>

#include "Farm.h"

Farm::Farm( int spaces )
{
m_Critters.reserve( spaces );
}

void Farm::Add( const Critter& aCritter )
{
m_Critters.push_back( aCritter );
}

void Farm::RollCall() const
{
for( std::vector< Critter >::const_iterator iter = m_Critters.begin(); iter < m_Critters.end(); ++iter )
std::cout << iter->GetName() << " here.\n";
}


--------------------------------------------------------------------------------
So that's it. I think that the first way is better, but I want to be sure. Thanks in advance.

Share this post


Link to post
Share on other sites
Quote:
Original post by superpig
I think you want the first way, but you want to take out the unnecessary "class Farm;" from critter.h.


It's unnecessary only in this case, isn't it? If Critter used Farm in a way I would it need that?

Share this post


Link to post
Share on other sites
Correct.

If a header does not even contain the name of another class - even if its corresponding .cpp file does - then it does not need any reference to it. The cpp file can take care of its own requirements.

If a header uses the name of another class and nothing further, you can usually get away with a "class OtherClass;" declaration.

If a header is using another class fully - e.g. you've got inline functions in your header that call the other class's methods - then you need a full #include "otherClass.h" directive.

If you can get away with no reference or a simple class declaration, you should - not just because it avoids circular dependencies, but because it reduces build times.

Share this post


Link to post
Share on other sites
I've got this code now..

init.h

#ifndef INIT_H
#define INIT_H

bool init(); // initialize SDL subsystems and TTF
SDL_Surface* loadImage( std::string filename ); //the image loading function

//apply a surface on the screen
void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* destination = SDL_GetVideoSurface() );

#endif



init.cpp

#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include <string>

#include "init.h"

bool init()
{
//initialize SDL subsystems

if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
{
fprintf( stderr, "SDL could not be initialized.\n" );
return false;
}
else
atexit( SDL_Quit );

if( TTF_Init() == -1 )
{
fprintf( stderr, "TTF could not be initialized.\n" );
return false;
}
else
atexit( TTF_Quit );

return true;
}

SDL_Surface* loadImage( std::string filename )
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;

loadedImage = IMG_Load( filename.c_str() );

if( loadedImage != NULL )
{
optimizedImage = SDL_DisplayFormat( loadedImage );
SDL_FreeSurface( loadedImage );
}
else
fprintf( stderr, "Image could not be loaded.\n" );

if( optimizedImage != NULL)
{
//map the color key, turquoise
Uint32 colorkey = SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF );

SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, colorkey );
}

return optimizedImage;
}

void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
SDL_Rect offset;
offset.x = x;
offset.y = y;

SDL_BlitSurface( source, NULL, destination, &offset );
}



main.cpp

#include "SDL/SDL.h"
//#include <string> ERROR
#include "init.h"

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int BPP = 32;

int main( int argc, char* args[] )
{
if( !init() )
return 0;

return 0;
}



The problem is, if I don't #include <string> in main, I get a bunch of errors. My question is, why is this happening? I have included string in init.cpp and main doesn't use it..

Those are the errors I'm getting:
<code>
1>f:\my documents\visual studio 2005\projects\blocks\blocks\init.h(5) : error C2653: 'std' : is not a class or namespace name
1>f:\my documents\visual studio 2005\projects\blocks\blocks\init.h(5) : error C2065: 'string' : undeclared identifier
1>f:\my documents\visual studio 2005\projects\blocks\blocks\init.h(5) : error C2146: syntax error : missing ')' before identifier 'filename'
1>f:\my documents\visual studio 2005\projects\blocks\blocks\init.h(5) : error C2059: syntax error : ')'
</code>

Share this post


Link to post
Share on other sites
Quote:
Original post by sheep19
and main doesn't use it..
Yes it does. It includes init.h, which uses it.

There is absolutely zero connection between a .h and .cpp file that both have the same name. None. It's purely a coding convention that you keep them in pairs. What init.cpp may or may not have in it has no effect on init.h.

Either you should put a forward declaration for std::string in the header - something like "class std::string;" - or you should put the include there instead of in the CPP files.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this