You are here:

C++/unresolved external symbol error when using template class

Advertisement


Question
I tried to create a template class. But while building i get the following linker error: 'unresolved external symbol. I could not identify the error. I used visual studio 6.0. Please identify the error for me. The code is as follows.

HEADER FILE:


#ifndef REL_PRIME_NUMB_H
#define REL_PRIME_NUMB_H


class Numb {
  private:
     int *numb;
     void *next;
     void *prev;
  public:
     Numb(int arg);
     ~Numb();
     int *Get_Numb(void);
     void *Get_Next(void);
     void *Get_Prev(void);
     //disable copy constructor
     Numb(const Numb ©);

};

template <class T> class List {
  private:
     T *first;
     T *last;
     void *next;
     void *prev;
     bool (*is_SortFuncPtr)(T *arg1, T *arg2);
     int cnt;
  public:
     List();
     ~List();
     T *Get_First(void);
     void *Get_Next(void);
     void *Get_Prev(void);
     int Insert(T *arg);
     int Sort(bool (*FuncPtr_isSwap)(T *arg1, T *arg2));
     int Delete(T *arg);
     //disable copy constructor
     List(const List ©);
};

#endif



CPP FILE;

#include "rel_prime_numb.h"
#include <stdio.h>


Numb::Numb(int arg) {
  numb=new int;
  *numb=arg;
  next=NULL;
  prev=NULL;
}

Numb::~Numb() {
  if(numb != NULL)
   delete numb;
}

int *Numb::Get_Numb() {
   return numb;
}

void *Numb::Get_Next() {
   return next;
}

void *Numb::Get_Prev() {
   return prev;
}



template <class T> List<T>::List() {
  first=NULL;
  last=NULL;
  next=NULL;
  prev=NULL;
  cnt=0;
}

template <class T> List<T>::~List() {
  if(first != NULL) {
     while(first->Get_Next() != NULL) {
        first=(T *)first->Get_Next();
        delete first->Get_Prev();
        cnt--;
     }
     delete first;
  }
}

template <class T> T *List<T>::Get_First() {
  return first;
}


template <class T> void *List<T>::Get_Next() {
  return next;
}


template <class T> void *List<T>::Get_Prev() {
  return prev;
}

template <class T> int List<T>::Insert(T *arg) {
  if (first == NULL) {
     first=arg;
     last=first;
  } else {
     last->Get_Next()=arg;
     (last->Get_Next())->Get_Prev()=last;
     last=last->GetNext();
  }
  cnt++
  return 0;
}

template <class T> int List<T>::Delete(T *arg) {
  if(arg == first) {
     if(arg == last) {
        delete first;
        first=NULL;
        last=NULL;
        cnt=0;
     } else {
        first=(T *)first->Get_Next();
        delete (T *)first->GetPrev();
        first->Get_Prev()=NULL;
     }
  } else if(arg == last) {
     last=(T *)last->Get_Prev();
     delete (T *)last->Get_Next();
     last->Get_Next()=NULL;
  } else {
     (T *)(((T *)(arg->Get_Prev()))->GetNext())=(T *)(arg->Get_Next());
     (T *)(((T *)(arg->Get_Next()))->GetPrev())=(T *)(arg->Get_Prev());
     delete (T *)arg;
  }
  return 0;
}


template <class T> int List<T>::Sort(bool (*FuncPtr_isSwap)(T *arg1, T *arg2)) {
  T *temp1, *temp2, *temp_prev1, *temp_next1;
  temp1=first;
  while (temp1 != NULL) {
     temp2=temp1->Get_Next();
     while(temp2 != NULL) {
        if((*FuncPtr_isSwap)(temp1, temp2) == true) {
         temp_prev1=temp1->Get_Prev();
         temp_next1=temp1->Get_Next();
         temp1->Get_Prev()=(T *)temp2->Get_Prev();
         temp1->Get_Next()=(T *)temp2->Get_Next();
         temp2->Get_Prev()=temp_prev1;
         temp2->Get_Next()=temp_next1;
        }
        temp2=temp2->Get_Next();
     }
     temp1=temp1->GetNext();
  }
  return 0:
}



MAIN FILE:



#include <AFXWIN.H>

#define MAX_REL_PRIME_CNT 6
#define REL_PRIME_STR "2, 3, 5, 7, 11, 13"

#include "rel_prime_numb.h"

int main(int argc, char *argv) {

  int numb_cnt_max=MAX_REL_PRIME_CNT;
  char *numb_str;
  List<Numb> numb_list;
  //copy the relative prime string to the variable rel_prime_str
  numb_str=new char[strlen(REL_PRIME_STR)+1];
  strcpy(numb_str, REL_PRIME_STR);

  printf("\n");
  
  delete [] numb_str;
  return 0;
}

Answer
Hello Samuel

I have been working on your code in visual C++ 6.0 and I do see the unresolved references too. There are 2 reasons.

1) Some unresolved symbols are caused by the AFXWIN.h file. You don't need it. You should replace it with string.h to get access to functions like strcpy.

2) You should take all the List methods out of the CPP file and put them into the HEADER file that has the List class declaration. A compiler will not know how to create the methods in the template class unless they are right in the file being compiled and the only way that will happen is if the methods are in the header file. If you don't do that, you will get more unresolved references.

Once that is done, then your program will compile and link, but you should understand that the compiler is ignoring much of your template source code because the program is not actually calling the template functions. If you add a line like:

numb_list.Insert(new Numb(10));

to your main routine, then the compiler will examine the Insert method and then you will see more compiler errors and they will be quite confusing. The errors come from the Insert method. In that method, you are doing things like this:

last->Get_Next()=arg;

Notice that in your original program, the Get_Next() returns a pointer. A pointer is just like a number, you cannot assign arg to a pointer, just like you cannot assign the number 2 to the number 1.

I do understand what you are trying to accomplish. You are trying to modify the pointer in a linked list. There are 2 ways, but I will tell you the easier way. If you have Get_Next return a reference to the pointer, instead of the pointer, then you can actually overwrite the pointer in the linked list.

Before I show you the syntax for that, I have to say that one big advantage of templates is that you no longer need to work with void*. The whole point of having a template container, like your linked list, is to eliminate the void*. So, your Get_Next should not be returning void*.

In your template, the Get functions should look like this:

  T *&Get_First(void);
  T *&Get_Next(void);
  T *&Get_Prev(void);

They return a pointer to the type T. Actually, they return a reference to the pointer, so that the pointer itself can be overwritten if necessary.

Similarly in class Numb, you should not be using void*, The pointers are of type Numb*, so you should declare them that way.

In your design, Numb should look like this:

class Numb {
private:
  int *numb; // This does not need to be a pointer.
  Numb *next;
  Numb *prev;
public:
  Numb(int arg);
  ~Numb();
  int *Get_Numb(void);
  Numb *&Get_Next(void);
  Numb *&Get_Prev(void);

private: // Need to make the copy constructor private to disable it.
  //disable copy constructor
  Numb(const Numb );
  
};

There is no real need to make the numb data in Numb a pointer. I would replace int* numb with int numb.


Finally, in your design, the List class looks like this:

template <class T> class List {
private:
  T *first;
  T *last;
  T *next;
  T *prev;
  bool (*is_SortFuncPtr)(T *arg1, T *arg2);
  int cnt;
public:
  List();
  ~List();
  T *&Get_First(void);
  T *&Get_Next(void);
  T *&Get_Prev(void);
  int Insert(T *arg);
  int Sort(bool (*FuncPtr_isSwap)(T *arg1, T *arg2));
  int Delete(T *arg);
  //disable copy constructor
  List(const List );
};

With the insert implementation like this:

template <class T> int List<T>::Insert(T *arg) {
      if (first == NULL) {
         first=arg;
         last=first;
      } else {
         last->Get_Next()=arg;
         (last->Get_Next())->Get_Prev()=last;
         last=last->Get_Next();
      }
      cnt++;
      return 0;
}


I have tested the Insert method, and I believe it works. I have not tested you other methods, so I leave it up to you to compile them and test them.

I have one more comment for you. In a proper design, the objects of type Numb should not know that they are being placed on a linked list. It is not the best design to have next and prev pointers inside Numb. If you wanted to put Numb objects on another type of data structure, or if you wanted to share a Numb object among different linked lists, it would not be possible in your design. I believe a better design would be to have the List template as you do now, and a helper to list like this:

template <class T> struct ListNode
{
   T* theData;
   ListNode<T>* prev;
   ListNode<T>* next;
};

Your List class would have only a first and last pointer, and they would point to elements of type ListNode, like this.

template <class T> class List {
private:
  ListNode<T> *first;
  ListNode<T> *last;
...
};

The ListNode points to a Numb instance through the "theData" pointer. The Numb class would not have prev and next. It would be responsible only for operations its data, and not have any knowledge of where it is stored.

I hope that helps you. There is still much work for you to do, but keep trying.

Best regards
Zlatko

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Zlatko

Expertise

No longer taking questions.

Experience

No longer taking questions.

Education/Credentials
No longer taking questions.

©2016 About.com. All rights reserved.