Weird Linker error during compilation; undefined references.

Hi, LeafyCircuits here.

Comp Specs:
OS: Windows 7 Home Premium 64-bit
Compiler: GNU gcc compiler with ISO and STL
IDE: Dev C++ v4.9.9.2 using C++11

NOTE: Code posted represents a portion of the project file. Assume that I know that all other code in the file works. Problem lines are commented for your convenience; if there are no "problem lines", assume that the code itself compiles.

I'm making an experimental "User" class and handlers, and I'm encountering an issue I haven't seen before. Below is the class `User' and a class `UserHandler' that handles function relating to the User classes, and a main that tests one of the functions of UserHandler.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
#include "core.h"

using namespace std;
using namespace AS_C;

class User
{
      public:
             User(string aname, string apass)
             {
                         name=new string;
                         password=new string;
                         (*name)=aname;
                         (*password)=apass;
             }
             
             string getName() {return *name;}
      
      private:
                string getPass() {return *password;}
                
                void changeName(string newname) {*name=newname;}
                void changePass(string newpass) {*password=newpass;}
                
                string *name, *password;
};

static class UserHandler
{
      public:
             friend class User;
             
             void addUser(string thename, string thepass)
             {
                  userbin[thename]=new User(thename, thepass);
             }
             
             void deleteUser(string thename)
             {
                  delete userbin[thename];
                  userbin[thename]=0;
                  userbin.erase(thename);
             }
      
      private:
              static map<string, User*> userbin;
}u_hnd;


main()
{
      u_hnd;
      u_hnd.addUser("Benny", "lolpass");
}


The code itself compiles, but the Linker spews an error, which prevents full compilation. Here's the error I get:
In function `ZNSt6vectorISsSaISsEE20_M_allocate_and_copyIN9__gnu_cxx17__normal_iteratorIPKSsS1_EEEEPSsjT_S9_':
  [Linker error] undefined reference to `UserHandler::userbin' 


According to the compile log, that long function is somewhere in a temporary .o in my comp's ...\appdata\local\temp\ directory, but... why are my .o's being created in the temp file directories? Shouldn't they be created in the working directory? Any assistance with this problem would be very helpful, thanks.



I'm new to the forums, to constructive criticism is greatly appreciated.
Static data members follow slightly different rules to normal data members. Although you've declared it fine, you also need to define (and initialise, if you want) it in global (or namespace) scope, like so:

map UserHandler::userbin(/* arguments to constructor */);
also static class has no meaning.

Also you're using the friend concept to break encapsulation which goes against good OOD. And there is no real reason to have your strings as pointers, just use local variables or references. Especially because you have no destructor freeing the memory
Zaita wrote:
you're using the friend concept to break encapsulation which goes against good OOD.

http://www.parashift.com/c++-faq-lite/friends-and-encap.html
@Peter87 did you even read the link you posted? His code does not show 1 class with it's implementation being split to make the code more manageable. He has a user handler class and a user class. Two very distinct roles.
Zaita wrote:
also static class has no meaning. Also you're using the friend concept to break encapsulation which goes against good OOD.

I was experimenting there. My goal was so that only one instance of the UserHandler class was running at any given time. I know there is a way of accomplishing this, but I'm not sure how. And you're right about the friend class, I'll remove that.

Zaita wrote:
there is no real reason to have your strings as pointers

Whoops, I forgot about the destructor. I'm using pointers simply for experience; I know there's no real reason, but I'm simply familiarizing myself to them.

MikeyBoy wrote:
you also need to define it in global scope

Thanks, that solves the issue! But one thing though: your line of code wouldn't compile because `map' alone doesn't name a type because it's a template, so instead, I had to do this:
map<string, User*> UserHandler::userbin;
But thanks for pointing me in the right direction (no pun intended)!

Here's the updated code for reference:
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
46
47
48
49
50
51
52
53
54
55
56
57
class User
{
      public:
             User(string aname, string apass)
             {
                         name=new string;
                         password=new string;
                         (*name)=aname;
                         (*password)=apass;
             }
             
             ~User()
             {
                    delete name, password;
                    name=0;
                    password=0;
             }
             
             string getName() {return *name;}
      
      private:
                string getPass() {return *password;}
                
                void changeName(string newname) {*name=newname;}
                void changePass(string newpass) {*password=newpass;}
                
                string *name, *password;
};

class UserHandler
{
      public:
             void addUser(string thename, string thepass)
             {
                  userbin[thename]=new User(thename, thepass);
             }
             
             void deleteUser(string thename)
             {
                  delete userbin[thename];
                  userbin[thename]=0;
                  userbin.erase(thename);
             }
             
             static map<string, User*> userbin;
}u_hnd;
map <string, User*> UserHandler::userbin;



main()
{
      u_hnd;
      u_hnd.addUser("Benny", "lolpass");
      cout << u_hnd.userbin["Benny"]->getName();     //Works, yay!
      getchar();
}
If you want only 1 instance of class look at the singleton design pattern.
Regarding the use of pointers by all means use them when instantiating classes, but not simple types like a string.

simple singleton for C++
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
class mySingleton {
public;
 static const mySingleton& Instance() {
  static mySingleton instance;
  return instance;
 } 

 void set_name(string new_name) { name_ = new_name; }
 void print_name() { cout << name_ << endl; }

private:
 string name_;
}

int main() {
 mySingleton instance1 = mySingleton::Instance();
 instance1.set_name("hello world");
 instance1.print_name();

 mySingleton instance2 = mySingleton::Instance();
 instance2.print_name();

 instance2.set_name("name2");
 instance1.print_name();
 instance2.print_name();
 return 0;
}

Thanks for the design pattern Zaita :)

I'll mark this post as solved from this point. Thanks for the help, everybody!
Topic archived. No new replies allowed.