Query re. unresolved external symbols / multiple .h .cpp files and classes

Pages: 12
I've been looking at a command design pattern to go in my portfolio, and have gotten a little stuck when implementing my own variation.

It started when I wanted to pass a vector of pointers (of command objects) to a player/actor class that would be able to access them as a queue. Prior to this I was accessing them directly through main, but I wanted the player to have a direct access to the queue.

I would really appreciate some help on this, specifically with making it work without much changes (so I learn how to fix this type of error), but don't mind other ideas and solutions in addition.

I'll describe what you're seeing and then let the code speak for itself.
5 files a main (source.cpp) and then the Player Object and the Attack (command) Object has 2 files each, a header and its subsequent cpp implementation.

I'm still learning more about implementing classes outside out their header and and have been learning a lot about what to do and what not to do, and what isn't necessary etc.

The FIRST real sticking error I came across was that the Player class wouldn't recognise the Vector of pointers-to-command objects named AttackCmd (the base class of the specific types of Attack, such as AtkNavy and Infantry etc.)

I played around with including and as far as it looks, I've fixed this and not have linker errors.

lastly I've tried to preview my question and use the Code tags etc but there seems to be an error so if this post works I will edit it as soon as it allows me

you will see I am getting 3 unresolved external symbol errors from main,

Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: __thiscall AtkInfantry::AtkInfantry(void)" (??0AtkInfantry@@QAE@XZ) referenced in function _main Command DP My version C:\Users\ryanb\source\repos\Command DP My version\Command DP My version\Source.obj 1

Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: __thiscall AtkNavy::AtkNavy(void)" (??0AtkNavy@@QAE@XZ) referenced in function _main Command DP My version C:\Users\ryanb\source\repos\Command DP My version\Command DP My version\Source.obj 1

Severity Code Description Project File Line Suppression State
Error LNK2019 unresolved external symbol "public: __thiscall AtkAirforce::AtkAirforce(void)" (??0AtkAirforce@@QAE@XZ) referenced in function _main Command DP My version C:\Users\ryanb\source\repos\Command DP My version\Command DP My version\Source.obj 1


==
Source.cpp
==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <vector>
#include "Player.h"
#include "Attack.h"

//class 
int main() {
	std::vector<AttackCmd*> Commands;
	Player  Ryan("Levi", Commands);

	AttackCmd* cmd1 = new AtkInfantry;
	Commands.push_back(cmd1);
	
	AttackCmd* cmd2 = new AtkAirforce;
	Commands.push_back(cmd2);

	AttackCmd* cmd3 = new AtkAirforce;
	Commands.push_back(cmd3);

	AttackCmd* cmd4 = new AtkAirforce;
	Commands.push_back(cmd4);

	AttackCmd* cmd5 = new AtkNavy;
	Commands.push_back(cmd5);

	for (int i=Commands.size()-1; i >= 0 ; i--) {
		//Commands[i]->Execute(Ryan);

	}
	delete cmd1;
	//cmd3->Cancel(Ryan);
	return 0;
}



==
Player.h
==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#pragma once
#include <iostream>
#include <vector>
#include <string>
class AttackCmd;
class Player {
public:
	std::string _name;
	int attacks = 0;
	std::vector<AttackCmd*> q;

	Player(std::string name, std::vector<AttackCmd*>& queue);




	void attack(std::string atk);
	void stop();
};

==
Player.cpp
==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#pragma once
#include <vector>
#include <iostream>
#include <string>
#include "Player.h"
#include "Attack.h"
//class AttackCmd;
Player::Player(std::string name, std::vector<AttackCmd*> &queue)
{
	//std::cout << q.size();
}


void Player::attack(std::string atk) {
		attacks++;
		std::cout << "Attack no. " << attacks << ". " << _name <<  atk << std::endl;
	}
void Player::stop()
{
		q.pop_back();
		std::cout << "Last command undone, iterating through command queue for confirmation: \n";
	//	for (int i = 1; i <= q.size(); i++) {
			//q[i]->Execute(this);
			//q[i]->Cancel;
		//	std::cout << q.size();
		
	
};



==
Player.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once
#include <iostream>
#include <vector>
#include <string>
class AttackCmd;
class Player {
public:
	std::string _name;
	int attacks = 0;
	std::vector<AttackCmd*> q;

	Player(std::string name, std::vector<AttackCmd*>& queue);




	void attack(std::string atk);
	void stop();
};



==
Attack.h
==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

#pragma once
//implementation of command design pattern for RTS or turn based Strategy game with multiple commands.
#include <iostream>
#include <vector>
#include <string>
#include "Player.h"
class AttackCmd {

public:
	AttackCmd() ;
	
		virtual void Execute(Player& play) = 0;
		virtual void Cancel(Player& play) = 0;
};

class AtkInfantry : public AttackCmd
{

public:
	AtkInfantry();
	std::string type = "Infantry";
	 void Execute(Player& play);
	 void Cancel(Player& play);
};

class AtkNavy: public AttackCmd
{
	public:
		AtkNavy() ;
	std::string type = "Navy";
	 void Execute(Player& play);
	 void Cancel(Player& play);
};

class AtkAirforce: public AttackCmd
{
public: 
	AtkAirforce() ;
	std::string type = "Airforce";
	 void Execute(Player& play);
	
	 void Cancel(Player& play);
};



==
Attack.cpp
==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#pragma once
#include <vector>
#include <iostream>
#include <string>
#include "Attack.h"
#include "Player.h"
//implementation of command design pattern for RTS or turn based Strategy game with multiple commands.
#pragma once




	//virtual void AttackCmd::Execute(Player& play) = 0;
	//virtual void AttackCmd::Cancel(Player& play) = 0;



//AtkInfantry::AtkInfantry() { std::cout << ""; }
	
	void AtkInfantry::Execute(Player& play) {
		play.attack(type);
		play.q.push_back(this);
	}
	void AtkInfantry::Cancel(Player& play) { 
		play.stop(); }



//AtkNavy::AtkNavy() {};
	
	 void AtkNavy::Execute(Player& play) {
		play.attack(type);
		play.q.push_back(this);
	}
	 void AtkNavy::Cancel(Player& play) { play.stop(); }


//	AtkAirforce::AtkAirforce() {};
	
	 void AtkAirforce::Execute(Player& play)
	{
		play.attack(type);
		play.q.push_back(this);
	}
	 void AtkAirforce::Cancel(Player& play) { play.stop(); }



Just to reiterate, I'd really appreciate some help on this, since it can be a vague error, or at least an error that needs some understanding of many areas of c++
Last edited on
Two things:

1. Don't use #pragma once in source (.cpp) files, only in headers.

2. You do have linker errors, an unresolved external symbol error is a linker error.

The linker is unable to find the three symbol because you've commented out the class methods (constructors) definitions needed in Attack.cpp. Uncomment lines 18, 29, and 38.
I still get the errors with those commented parts edited. That was just a last ditch attempt I forgot to revert back.

Thanks for looking at this. Any other ideas? (I feel like we're on the right track)

Grateful,

Ryan
Last edited on
#pragma once may be recognized by most compilers, it still is non-standard C/C++.

https://en.wikipedia.org/wiki/Pragma_once

Better if you use include guard notation instead so your code is C/C++ compiler agnostic.

https://en.wikipedia.org/wiki/Include_guard

Thank you, I've seen a few videos on these and people seem to have different opinions. Although if it is non standard, then I understand.

Are you able to get it working? I cannot.
Well, did you uncomment the lines I suggested?

You should now get a linker error for the AttackCmd() ctor.

You don't have an actual ABT AttackCmd ctor. Ooops!
Last edited on
Yes, I said in my first reply that I uncommented those.

Also AttackCmd is an abstract base class. Therefore, do I need a ctor still?
I'll check it now
(I still get the original error) Also thanks.
Last edited on
Your ABT might look like this:
1
2
3
4
5
6
7
8
9
10
class AttackCmd
{
protected:
   AttackCmd() = default;

public:
   virtual ~AttackCmd()               = default;
   virtual void Execute(Player& play) = 0;
   virtual void Cancel(Player& play)  = 0;
};

Now the fool thing should compile.

Now if the code actually works as intended, it's up to you.

ABTs are tricky things, all too easy to muck it all up.
Last edited on
Still same errors, what did I miss. I uncomments, then make constructor protected.


Attack.h


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#pragma once
//implementation of command design pattern for RTS or turn based Strategy game with multiple commands.
#include <iostream>
#include <vector>
#include <string>
#include "Player.h"
class Player;
class AttackCmd {
protected:
	AttackCmd() = default;
public:
	//AttackCmd() ;
	
		virtual void Execute(Player& play) = 0;
		virtual void Cancel(Player& play) = 0;
};

class AtkInfantry : public AttackCmd
{

public:
	AtkInfantry();
	std::string type = "Infantry";
	 void Execute(Player& play);
	 void Cancel(Player& play);
};

class AtkNavy: public AttackCmd
{
	public:
		AtkNavy() ;
	std::string type = "Navy";
	 void Execute(Player& play);
	 void Cancel(Player& play);
};

class AtkAirforce: public AttackCmd
{
public: 
	AtkAirforce() ;
	std::string type = "Airforce";
	 void Execute(Player& play);
	
	 void Cancel(Player& play);
};



=
Attack.cpp
=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#pragma once
#include <vector>
#include <iostream>
#include <string>
#include "Attack.h"
#include "Player.h"
//implementation of command design pattern for RTS or turn based Strategy game with multiple commands.

//AttackCmd::AttackCmd() ;

//	virtual void AttackCmd::Execute(Player& play) = 0;
//	virtual void AttackCmd::Cancel(Player& play) = 0;

AtkInfantry::AtkInfantry()
{ 

}
	
	void AtkInfantry::Execute(Player& play) {
		play.attack(type);
		play.q.push_back(this);
	}
	void AtkInfantry::Cancel(Player& play) { 
		play.stop(); }



AtkNavy::AtkNavy() {};
	
	 void AtkNavy::Execute(Player& play) {
		play.attack(type);
		play.q.push_back(this);
	}
	 void AtkNavy::Cancel(Player& play) { play.stop(); }


	AtkAirforce::AtkAirforce() {};
	
	 void AtkAirforce::Execute(Player& play)
	{
		play.attack(type);
		play.q.push_back(this);
	}
	 void AtkAirforce::Cancel(Player& play) { play.stop(); }
I missed destructor, let me just try that first.
Still the same error, hmmm.


class AttackCmd {

protected:
AttackCmd() = default;
public:
//AttackCmd() ;

virtual void Execute(Player& play) = 0;
virtual void Cancel(Player& play) = 0;

virtual ~AttackCmd() = default;
};
Still the same error

I don't know what your compiler is, you could try forcing a rebuild.

With the code your supplied, and the changes I suggested, no errors from my compiler.

Nothing gets displayed, but no compiler errors. No run-time errors.
DO NOT have #pragma once in your source (.cpp) files. REMOVE IT!
If you take the comments out from line 28 on source.cpp do you get some output?

is it because I didn't do #ifdef on the files and left pragma once (but I now removed it from all cpp)
visual studio 2019
Do you mind sending me the code, or link to the files
Got it working by copying the code into a new project. The linker errors dissapeared and I fixed the new errors / underlying errors re. having a constructor elsewhere for the pure class etc.

However, any idea why I had link errors on the other project? I hit 'rebuild solution' and still got them'

Last question, why do I have to forward declare a class even though I've included a header file with it in with preprocessor??
Pages: 12