[C++] Link errors

Status
Not open for further replies.

Will

Senior Administrator
Staff member
Joined
Mar 4, 2012
Posts
8,197
Location
%tmp%
I've been making a text based RPG game in C++ to help me learn, but I'm getting Link errors I can't work out. The broken section is an attribute library - I'm trying to make the program as modular as possible, different functionalities (inventory, fighting, attributes etc) are all in different .cpp files.

The error is:

1>Hero.obj : error LNK2019: unresolved external symbol "public: __thiscall Attribute_Table::Attribute_Table(int)" (??0Attribute_Table@@QAE@H@Z) referenced in function _wmain


Main code below:

Code:
// Hero.cpp : Defines the entry point for the console application.
// 
// This program is a hero attribute program for an RPG. Here, the player chooses their hero's attributes for their journey.

#include "stdafx.h"
#include "std_lib_facilities.h"
#include "attributes.h"
#include "common.h"




int _tmain(int argc, _TCHAR* argv[])
{
try
{	
	
	Attribute table table1; //error here
	Attribute_Table table2 (30); //error here
	
	keep_window_open();
}
catch (exception& e) {
    cerr << "error: " << e.what() << '\n'; 
	keep_window_open();
    return 1;
}
catch (...) {
    cerr << "Oops: unknown exception!\n"; 
	keep_window_open();
    return 2;
}

	return 0;
}

The problem I'm having is with these two lines:

Attribute table table1; //error here
Attribute_Table table2 (30); //error here

These were written just as a basic test of the attribute source file - unfortunately it's failing already so I haven't gone on to write more detailed code.

attributes.h contains:

Code:
//Attribute system header files.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Attribute {
public:
	string name;
	int points;
	Attribute();
	Attribute(string n);
	Attribute(string n, int p);

};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	

	int get(string name); //return point value of attribute
	int check(); //return remaining spending points
	void increase(string name, int points); //increase point value of attribute
	void decrease(string name, int points); //decrease point value of attribute

	void define(string name); //define a new attribute
	void define(string name, int points);

	
	Attribute_Table();
	Attribute_Table(int s);
	

private:
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes
	

	
	
};

The definitions are here:

Code:
//Attribute structure
class Attribute {
public:
	string name;
	int points;
	Attribute()
		:name(), points() { }
	Attribute(string n)
		:name(n), points(0) { }
	Attribute(string n, int p)
		:name(n), points(p) { }
};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	
	int get(string name); //return point value of attribute
	int check(); //return remaining spending points
	void increase(string name, int points); //increase point value of attribute
	void decrease(string name, int points); //decrease point value of attribute

	void define(string name); //define a new attribute
	void define(string name, int points);

	Attribute_Table()
		:spend(0), table() { }
	Attribute_Table(int s)
		:spend(s), table() { }
	

private:
	
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes
	

	
	
};

I haven't included all of this source file - the remaining functions are defined outside the class, but currently my issue is with the constructor. What I don't understand, is I'm able to create new variables of type Attribute fine - tested the Attribute class code and it seems to be working fine. I can't see any major difference between the constructors of class Attribute, and class Attribute_Table - yet Attribute_Table is giving a link error?

If I replace the two lines giving errors in main() with:

Attribute s;

The variable s of type Attribute is created without error - what's the difference between this and the Attribute_Table class? I'm confused. :lol:
 
At a glance, the problem seems to lie with
Code:
	int get(string name); //return point value of attribute
	int check(); //return remaining spending points
	void increase(string name, int points); //increase point value of attribute
	void decrease(string name, int points); //decrease point value of attribute

	void define(string name); //define a new attribute
	void define(string name, int points);

Those functions are declared but not defined in the above code.
 
Last edited:
Hi Mike, They're defined - I just didn't include the full source file as I didn't think they're relevant. None of them are yet being called, and it seems to be the constructor throwing the error?

Full source for attributes.cpp below.

Code:
//Contains functions and classes for character attributes.

#include "StdAfx.h"
#include "std_lib_facilities.h"
#include "common.h"

//Attribute structure
class Attribute {
public:
	string name;
	int points;
	Attribute()
		:name(), points() { }
	Attribute(string n)
		:name(n), points(0) { }
	Attribute(string n, int p)
		:name(n), points(p) { }
};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	
	int get(string name); //return point value of attribute
	int check(); //return remaining spending points
	void increase(string name, int points); //increase point value of attribute
	void decrease(string name, int points); //decrease point value of attribute

	void define(string name); //define a new attribute
	void define(string name, int points);

	Attribute_Table()
		:spend(0), table() { }
	Attribute_Table(int s)
		:spend(s), table() { }
	

private:
	
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes
	

	
	
};

int Attribute_Table::get(string name)
	//Return point value of an attribute
{
	for (int i = 0; i < table.size(); ++i){
		if (name == table[i].name)
			return table[i].points;
	}
	error("Attribute does not exist");
}

int Attribute_Table::check(){
	//returns unused points
	return spend;
}

void Attribute_Table::increase(string name, int points)
	//increases an attribute's points
{
	bool found = false;
	for (int i = 0; i < table.size(); ++i){
		if (name == table[i].name){
			table[i].points += points;
			found = true;
		}
	}
	if(!found){
		error("Attribute does not exist");
	}
	
}

void Attribute_Table::decrease(string name, int points)
	//decreases an attribute's points
{
	bool found = false;
	for (int i = 0; i < table.size(); ++i){
		if (name == table[i].name){
			table[i].points -= points;
			found = true;
		}
	}
	if(!found){
		error("Attribute does not exist");
	}
}

void Attribute_Table::define(string name)
	//define a new attribute
{
	for (int i = 0; i < table.size(); ++i){
		if (name == table[i].name){
			error("Attribute already exists");		
		}
	}

	table.push_back(name);

}


void Attribute_Table::define(string name, int points)
	//define a new attributen
{
	for (int i = 0; i < table.size(); ++i){
		if (name == table[i].name){
			error("Attribute already exists");		
		}
	}
	Attribute temp;
	temp.name = name;
	temp.points = points;
	table.push_back(temp);


}
 
Your .cpp code contains the same functions declared a second time as they were in the .h file. See if removing them helps.
 
Tried removing the following code - once from the attributes.cpp (generated errors), and once from attributes.h (had no effect on link error).

Code:
int get(string name); //return point value of attribute
	int check(); //return remaining spending points
	void increase(string name, int points); //increase point value of attribute
	void decrease(string name, int points); //decrease point value of attribute

	void define(string name); //define a new attribute
	void define(string name, int points);
 
Hmm, I still recommend checking function declarations and definitions. I receive the same LINK errors you described if I do not define the functions and only declare them. Once all were defined, all worked well (I just left all functions empty since I did not have the functions themselves at that time). I'll try with your defined functions and see if problems return.
 
Sorry, I don't follow - as a test I just commented out all unused functions.

attributes.cpp:

Code:
//Contains functions and classes for character attributes.

#include "StdAfx.h"
#include "std_lib_facilities.h"
#include "common.h"

//Attribute structure
class Attribute {
public:
	string name;
	int points;
	Attribute()
		:name(), points() { }
	Attribute(string n)
		:name(n), points(0) { }
	Attribute(string n, int p)
		:name(n), points(p) { }
};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	

	Attribute_Table()
		:spend(0), table() { }
	Attribute_Table(int s)
		:spend(s), table() { }
	

private:
	
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes
	

	
	
};

attributes.h:

Code:
//Attribute system header files.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Attribute {
public:
	string name;
	int points;
	Attribute();
	Attribute(string n);
	Attribute(string n, int p);

};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
		
	Attribute_Table();
	Attribute_Table(int s);
	

private:
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes
	
	
};

Same errors.
 
Are you redefining the classes in both the .h and .cpp file by chance? It appears you have the .h file in the second code box you gave, then the .cpp file in the third code box. Is that correct?

Edit: Just saw your post. Your .cpp file re-defines the classes. The .cpp file should look like the second half past the }; with all Attributes functions defined as

Code:
Attribute::Attribute()
		:name(), points() { }
 
Last edited:
I've also just tried moving all variables used in the constructor over to the Public section of the class - still getting that error. At this stage, isn't the code almost identical to the working Attribute class?
 
The current code:

Main (hero.cpp)

Code:
// Hero.cpp : Defines the entry point for the console application.
// 
// This program is a hero attribute program for an RPG. Here, the player chooses their hero's attributes for their journey.

#include "stdafx.h"
#include "std_lib_facilities.h"
#include "attributes.h"




int _tmain(int argc, _TCHAR* argv[])
{
try
{	
	
	Attribute_Table table2 (30); //error here
	
	keep_window_open();
}
catch (exception& e) {
    cerr << "error: " << e.what() << '\n'; 
	keep_window_open();
    return 1;
}
catch (...) {
    cerr << "Oops: unknown exception!\n"; 
	keep_window_open();
    return 2;
}

	return 0;
}

Attributes.cpp:

Code:
//Contains functions and classes for character attributes.

#include "StdAfx.h"
#include "std_lib_facilities.h"
#include "common.h"

//Attribute structure
class Attribute {
public:
	string name;
	int points;
	Attribute()
		:name(), points() { }
	Attribute(string n)
		:name(n), points(0) { }
	Attribute(string n, int p)
		:name(n), points(p) { }
};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	int spend;	 //remaining unused points.
	vector<Attribute> table; //Vector of attributes

	Attribute_Table()
		:spend(0), table() { }
	Attribute_Table(int s)
		:spend(s), table() { }

	
};

Attributes.h:

Code:
//Attribute system header files.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Attribute {
public:
	string name;
	int points;
	Attribute();
	Attribute(string n);
	Attribute(string n, int p);

};

class Attribute_Table {
	// class containing table of attributes and interface.
public:
	
	int spend;
	vector<Attribute> table;
	
	Attribute_Table();
	Attribute_Table(int s);

};

Calling Attribute works - Calling Attribute_Table doesn't. I've commented out all other code at this stage.
 
The problem is with the .cpp file. You are re-definining the classes as well as a few variables. Less drastic, you have some signed/unsigned mismatches with your for loops due to using int i and a signed .size() value.

Code:
#include "stdafx.h"
#include "attributes.h"

//Attribute structure
Attribute::Attribute()
		:name(), points() { }
Attribute::Attribute(string n)
		:name(n), points(0) { }
Attribute::Attribute(string n, int p)
		:name(n), points(p) { }

Attribute_Table::Attribute_Table()
		:spend(0), table() { }
Attribute_Table::Attribute_Table(int s)
		:spend(s), table() { }

int Attribute_Table::get(string name)
	//Return point value of an attribute
{
	for (int i = 0; i < int(table.size()); ++i){
		if (name == table[i].name)
			return table[i].points;
	}
	error("Attribute does not exist");
}

int Attribute_Table::check(){
	//returns unused points
	return spend;
}

void Attribute_Table::increase(string name, int points)
	//increases an attribute's points
{
	bool found = false;
	for (int i = 0; i < int(table.size()); ++i){
		if (name == table[i].name){
			table[i].points += points;
			found = true;
		}
	}
	if(!found){
		error("Attribute does not exist");
	}
	
}

void Attribute_Table::decrease(string name, int points)
	//decreases an attribute's points
{
	bool found = false;
	for (int i = 0; i < int(table.size()); ++i){
		if (name == table[i].name){
			table[i].points -= points;
			found = true;
		}
	}
	if(!found){
		error("Attribute does not exist");
	}
}

void Attribute_Table::define(string name)
	//define a new attribute
{
	for (int i = 0; i < int(table.size()); ++i){
		if (name == table[i].name){
			error("Attribute already exists");		
		}
	}

	table.push_back(name);

}


void Attribute_Table::define(string name, int points)
	//define a new attributen
{
	for (int i = 0; i < int(table.size()); ++i){
		if (name == table[i].name){
			error("Attribute already exists");		
		}
	}
	Attribute temp;
	temp.name = name;
	temp.points = points;
	table.push_back(temp);


}
 
Thanks for looking through the code, not quite sure I follow though. Where are the classes and variables being re-defined?
 
There are many different ways to write classes and .cpp code in general. You've picked up on the fine points, but the organization is somewhat lax.

Your .h file should define all classes and variables. The functions should then be declared. I usually separate these into two areas within the private, protected, and public areas. My variables take up one area within each of those three types, and then my functions are declared but not defined in the next area. I would recommend you do the same since that is what is typically taught in programming classes and books.

Then, in the .cpp code, you should define your functions as I have in the code box provided in my previous post so that the functions are linked to their class(es). Your .cpp file should include the .h file with the declared functions so the compiler knows that the .cpp file is to be used with your main code as part of the .h file. Then, when your main code compiles, it links to the .h and .cpp code for that .h file.

Your linker errors occurred because the compiler only found the .h file. If it had found both, it would have complained that you had re-defined the classes and variables within the .cpp file.
 
Ah, I see what I've done now. Thanks - this program was to test myself on the concepts I've learnt, and actually put them into a program from scratch rather than using exercises or examples.
 
Your .h file should define all classes and variables. The functions should then be declared. I usually separate these into two areas within the private, protected, and public areas. My variables take up one area within each of those three types, and then my functions are declared but not defined in the next area. I would recommend you do the same since that is what is typically taught in programming classes and books.

Sorry, I should have elaborated as to why it is taught this way in classes and books. You probably would see it yourself once you implement it, but separating things this way makes it easier later to go back and add or remove functions and variables to or from the classes. A class can get to be difficult to manage once it grows to a point, so organization can become key to allowing that class to continue to be useful.


Ah, I see what I've done now. Thanks - this program was to test myself on the concepts I've learnt, and actually put them into a program from scratch rather than using exercises or examples.

Glad to help. If you have any further questions with organization, feel free to post back or send a PM.
 
Thanks, I've got the attribute system up and running. I have a question relating to the next feature I want to add - an inventory/item system.

Somewhere, I want to have a table of RPG items, with attributes (name, id, value etc) - preferably separate to the inventory library. I want the inventory system to be able to search/load items from the table - making it easy to use the inventory code with any item table, and for it to be easy to add new items to the game.

What's the best way of going about this? (Or for that matter, any way. :grin:. I have a few ideas, but I'm not sure how to implement them.)
 
I would think the best way would be to create an inventory/item class that has the inventory/item properties stored within. I myself would create just an item class so each item can be stored as an object or inventory of objects and each item has its own properties.

For the properties: You could create a properties class if you wanted to, or you could store properties directly to the class; it depends on how complicated the item properties are.

Then create vector<item> to add different items to the character. The vector would essentially be your inventory for each character.

For easy searching for items from a table, I would suggest using a map instead of a vector.
 
Last edited:
Yeah, I have a class set up for items already - also intending to have separate classes for chests/inventory/bank etc which will all be related. The item class I have so far is:

Code:
class Item{
	//Contains a single item.

public:
	int id; //unique item ID
	string name; //item name, as displayed to player	
	float value; //economic value of item
	bool quest; //quest item?

	bool equip; //Can item be equiped?
	float armour; //item armour rating
	float dmg; //item damage rating

	
};

I'm looking for a better place to store the actual item data, possibly outside the inventory source files. I want somewhere to store:

Id: 1
name: "Wooden Sword"
value: 50
quest: false
equip: true
armour: 0
dmg: 10

But I would like the possibility of adding anywhere between 10 and 100 items, or more. Even if I never intend to create that many items, I want somewhere neat to put the data that can also be added to at a later date.
 
The best way to do it would probably be to create an .xml file with your items and their properties. Then read the items into an inventory class rather than creating an item class. That way, you can get items from the .xml file and place them into vectors such as:
Code:
Id.push_back(thisId);
name.push_back(thisName);
value.push_back(thisValue);
quest.push_back(thisQuest);
equip.push_back(thisEquip);
armour.push_back(thisArmour);
dmg.push_back(thisDmg);
 
Thanks for your help - I'll try making an XML file for the data. I've just started to look at reading from an XML document into C++ (I'm using Visual Studio), should I use a library of some kind (i.e. Xerces) or should I write a method myself?

I still think the data needs to be read into an individual item class - the actual "inventories" will be vector<item> - each item contains it's own attributes, and it's easy to remove or add a single item rather than having to remove the elements of several vectors. As there are already going to be several inventory systems (one for chests, one for rucksack, one for equipped items, one for bank etc) I want to avoid having a large number of vectors.

I have two ideas on how to use the XML file - either the program searches through the XML for a particular Id and then loads that item. Alternatively all items are sorted into a vector when the game first loads, the vector is then used whenever an inventory needs item data.

---

Unrelated question - when a chest is created/loaded, I want the ability to put a random number of items into the chest when it's created. Is the only way to do this overloading?

E.g.

Chest chest1 (1);
Chest chest2 (4, 6, 4, 27, 9, 10);
Chest chest3 (2, 3, 8, 7);

Each integer represents an item Id. I want the Chest constructor to take as many item Id's as needed, do I have to overload the constructor for every length the function can take?

I know my original question has been answered - but it seems a better idea to continue using the thread for questions/ideas than not.
 
Status
Not open for further replies.

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top