12

Possible Duplicate:
Undefined Reference to

I've been banging my head against this one set of error messages for about 4 hours now and I can't seem to figure it out. I've not posted here before, so I apologize in advance if it's not in the right area or I've done something wrong. At any rate, the error messages I'm receiving are:

main.cpp|28|undefined reference to `LinkedSortedList<Employee>::LinkedSortedList()'|
   main.cpp|52|undefined reference to `empPrint(LinkedSortedList<Employee>&)'|
   main.cpp|58|undefined reference to `empSave(LinkedSortedList<Employee>&, std::string)'|
   main.cpp|65|undefined reference to `empLoad(LinkedSortedList<Employee>&, std::string)'|
   main.cpp|70|undefined reference to `LinkedSortedList<Employee>::~LinkedSortedList()'|
   main.cpp|70|undefined reference to `LinkedSortedList<Employee>::~LinkedSortedList()'|

   obj\Debug\main.o||In function `Z9empSearchR16LinkedSortedListI8EmployeeE':|
   main.cpp|109|undefined reference to `LinkedSortedList<Employee>::getHead()'|

My main.cpp is as follows:

#include <iostream>
#include <string>
#include <stdio.h>
#include <fstream>
#include "SortedList.h"
#include "LinkedSortedList.h"
#include "Employee.h"
#include "LinkedNode.h"

using namespace std;

void newEmp(LinkedSortedList <Employee>& empList);
void empSearch(LinkedSortedList <Employee>& empList);
void empPrint(LinkedSortedList <Employee>& empList);
void empSave(LinkedSortedList <Employee>& empList, string file);
void empLoad(LinkedSortedList <Employee>& empList, string file);

int main()
{
    //int empID;
    bool menuFinish = false;
    LinkedSortedList<Employee> empList;
    char selection;

    while (!menuFinish)

        //simple menu system through cout, the selection is read in through cin
        //and converted to upper case for simplicity during the conditionals
        cout << "Menu" << endl;
        cout << "(I)nsert new record" << endl;
        cout << "(E)mployee ID search" << endl;
        cout << "(P)rint employee info" << endl;
        cout << "(S)ave database to a file" << endl;
        cout << "(L)oad database from file" << endl;
        cout << "(Q)uit" << endl;
        cout << "Enter selection " << endl;
        cin >> selection;
        selection = toupper(selection);

        //menu selections are compared with their functions
        if (selection == 'I')
            newEmp(empList);
        else if (selection == 'E')
            empSearch(empList);
        else if (selection == 'P')
            empPrint(empList);
        else if (selection == 'S')
            {
                string fileName;
                cout << "Enter a filename to save the database to " << endl;
                cin >> fileName;
                empSave(empList, fileName);
            }
        else if (selection == 'L')
            {
                string fileName;
                cout << "Enter the filename to load the database from " << endl;
                cin >> fileName;
                empLoad(empList, fileName);
            }
        else if (selection == 'Q')
            menuFinish = true;
        else
            cout << "Incorrect choice " << endl;
}

//function creates a new employee
void newEmp(LinkedSortedList <Employee>& empList)
{
    string firstName;
    string lastName;
    int empID = -1;

    cout << "Please enter the first name " << endl;
    cin >> firstName;
    cout << "Please enter the last name " << endl;
    cin >> lastName;
    while (empID > 9999999 || empID < 0)
        {
        cout <<"Please enter the employee ID " << endl;
        cin >> empID;

        }
    //puts the employee in the db unless they're already found, then outputs an
    //error on the screen
    Employee emp(firstName, lastName, empID);
    bool findEmp = empList.find(emp);

    if (!findEmp)
        empList.insert(emp);
    else
        cout << "Emlpoyee ID " << empID << " already in use " << endl;
}

//function to search for an employee based on their employee ID
void empSearch (LinkedSortedList <Employee>& empList)
{
    int empID;
    int sizeOfList = 0;
    bool noEmp = true;

    cout << "Enter employee ID " << endl;
    cin >> empID;

    LinkedNode <Employee>* temp = empList.getHead();
    while (sizeOfList < empList.size() && noEmp)
    {
        sizeOfList++;
        if (empID == temp->value.getEmpID())
            {
                cout << "Searched " << sizeOfList << "employees " << endl;
                cout << "Found record: " << temp->value;
                noEmp = false;
            }
            temp = temp->next;
    }
    if (noEmp)
    cout << "Search of " << sizeOfList << " employees.  Employee not found" << endl;

}

//function used to print the first and last five employees from the db
void empPrint (LinkedSortedList <Employee>& empList)
{
    if (empList.size() <= 10)
        {
            empList.print();
        }

        else
        {
            LinkedNode<Employee>* temp = empList.getHead();
            cout << "First five employees: " << endl;

            for (int i = 0; i < 5; i++)
                {
                cout << temp->value << endl;
                i++;
                temp = temp->next;
                }

            int midList = empList.size()-5;
            for (int i = 0; i < midList; i++)
                {
                     temp = temp->next;
                }

            cout << "Last five employees: " << endl;
            for (int i = 0; i < 5; i++)
                {
                    cout << temp->value << endl;
                    i++;
                    temp = temp->next;
                }
        }
}

//function used to save the employee information from the db to a file
void empSave(LinkedSortedList<Employee>& empList, string fileName)
{
    string lastName;
    string firstName;
    //int empID;
    ofstream output;
    output.open(fileName.c_str());
    if (!output)
        {
            cout << "File not saved" << endl;
        }
    else
    {
        LinkedNode<Employee>* temp = empList.getHead();
        int i = 0;
        while (i < empList.size())
        {
            output << temp->value.getLastName() << " " << temp->value.getFirstName() << " " << temp->value.getEmpID() << endl;
            i++;
            temp = temp->next;
        }
    }
    output.close();
}

//function used to load the employee information from a file to the db
void empLoad(LinkedSortedList<Employee>& empList, string fileName)
{
    if (empList.size() > 0)
    {
        empList.clear();
    }

    ifstream input;
    input.open(fileName.c_str());

    if (!input)
    {
        cout << "No file exists" << endl;
    }

    else
    {
        int empID;
        string firstName;
        string lastName;
        string delimiter;

        while(input.good());
        {
            getline(input, delimiter, '\n');
            getline(input, lastName, ' ');
            getline(input, firstName, ' ');
            input >> empID;

            Employee emp(lastName, firstName, empID);

            bool empFound = empList.find(emp);

            if(!empFound)
            {
                empList.insert(emp);
            }
            else
            cout << "Employee already exists" << endl;
        }
    }
}

My LinkedSortList.cpp is:

#ifndef _LinkedSortedList_
#define _LinkedSortedList_

#include "Employee.h"
#include "LinkedSortedList.h"
#include "SortedList.h"
#include "LinkedNode.h"

#include <iostream>
#include <fstream>
#include <string>


using namespace std;

//default constructor
//sets head to null and makes the size of the list 0
template <class Elem>
LinkedSortedList<Elem>::LinkedSortedList()
{
    head = NULL;
    listSize = 0;
}

//destructor, clears list THEN deletes the head so memory leaks are
//stopped
template <class Elem>
LinkedSortedList<Elem>::~LinkedSortedList()
{
    clear();
    delete head;
}

//clears the list, freeing memory and stopping leaks and resets the
//list size
template <class Elem>
void LinkedSortedList<Elem>::clear()
{
    LinkedNode<Elem> *indexPtr = head;

    while (head != NULL)
    {
        head = head->next;
        delete indexPtr;
        indexPtr = head;
    }
    listSize = 0;
}

//finds a search value in the list... if it finds it then it returns true,
//otherwise it returns false
template <class Elem>
bool LinkedSortedList<Elem>::find(Elem searchValue) const
{
    LinkedNode<Elem>* indexPtr = head;
    while (indexPtr != NULL)
    {
        if (indexPtr->value == searchValue)
        {
            return true;
        }
        indexPtr = indexPtr->next;


    }
    return false;
}

//gets and DELETES first value in the list - if it finds nothing then
//return false, otherwise true
template <class Elem>
bool LinkedSortedList<Elem>::getFirst(Elem &returnValue)
{
    LinkedNode<Elem>* indexPtr = head;
    if (indexPtr == NULL)
        return false;
        else
        {
            head = head->next;
            returnValue = indexPtr->value;
            delete indexPtr;
            listSize--;
            return true;
        }
        returnValue = indexPtr->value;
}

//prints the list to cout or prints a warning if the list contains
//no values
template <class Elem>
void LinkedSortedList<Elem>::print() const
{

    if (head == NULL)
    {
         cout << "No elements in the list" << endl;
    }
        else
        {
            LinkedNode<Elem>* indexPtr = head;
            while (indexPtr != NULL)
            {
                cout << indexPtr->value << endl;
                indexPtr = indexPtr->next;
            }
        }
}

//returns the size of the list to the caller
template <class Elem>
int LinkedSortedList<Elem>::size() const
{
    return listSize;
}

//inserts a value into the list where it should go, if the list is empty it will
//say there are no existing nodes
template <class Elem>
bool LinkedSortedList<Elem>::insert(Elem newValue)
{
    LinkedNode<Elem>* indexPtr = head;
    LinkedNode<Elem>* newNode;
    //newNode->value = newValue;

     try
    {
        newNode = new LinkedNode<Elem>(newValue);
    }
    catch(exception e)
    {
        cout<<"Exception reached: " << e.what() << endl;
        return false;
    }

    //checks to see if the list is empty, if it is then it makes the newNode
    //the head
    if (head == NULL)
        {
            cout << "No existing nodes" << endl;
            head = newNode;
            cout << "First node is now " << head->value << endl;
            listSize++;
            return true;
        }

        /*looks to see if the value of the newNode is less than or equal to the
        index, if it is then it sets the point of the newNode equal to the head
        then makes the head the newNode and increments the listSize by one to keep
        track of the size of the list and returns true*/
        else if (newNode->value <= head->value)
        {
            newNode->next = head;
            head = newNode;
            listSize++;
            return true;
        }

        /*if the newNode value is greater than the index, then:*/
        else
        {
            while(indexPtr->next != NULL && newNode->value > indexPtr->next->value)
                {
                    indexPtr = indexPtr->next;
                }

            if (indexPtr->next == NULL)
            {
                indexPtr->next = newNode;
                listSize++;
                return true;
            }
            else
            {
                newNode->next = indexPtr->next;
                indexPtr->next = newNode;
                listSize++;
                return true;
            }

        }
}
//added for project 2 to return the head of the LL
template <class Elem>
LinkedNode<Elem>* LinkedSortedList<Elem>::getHead()
{
    return head;
}


#endif

The Employee.cpp is:

#ifndef _Employee_
#define _Employee_

#include "Employee.h"
#include "LinkedSortedList.h"
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

//blank default constructor
Employee::Employee()
{
}

//constructor that takes 3 parameters and sets them
Employee::Employee(string lastName, string firstName, int eID)
{
    this->lastName = lastName;
    this->firstName = firstName;
    this->empID = eID;
}

//blank deconstructor
Employee::~Employee()
{
}

//overloaded equality operator
bool Employee::operator==(Employee &nextEmployee)
{
    if (this->empID == nextEmployee.empID)
            return true;
        else
            return false;
}

//overloaded less than or equal to operator
bool Employee::operator <= (Employee &nextEmployee)
{
    if (this->empID <= nextEmployee.empID)
            return true;
        else
            return false;
}

//overloaded greater than or equal to operator
bool Employee::operator >= (Employee &nextEmployee)
{
    if (this->empID >= nextEmployee.empID)
            return true;
        else
            return false;
}

//overloaded less than operator
bool Employee::operator < (Employee &nextEmployee)
{
    if (this->empID < nextEmployee.empID)
            return true;
        else
            return false;
}

//overloaded greater than operator
bool Employee::operator > (Employee &nextEmployee)
{
    if (this->empID > nextEmployee.empID)
            return true;
        else
            return false;
}

// overloaded output stream operator
ostream& operator<<(ostream& os, const Employee empl)
{
    os << "Last: " << empl.lastName << endl;
    os << "First: " << empl.firstName << endl;
    os << "Employee ID: " << empl.empID << endl;
    return os;
}

#endif

My header file, LinkedSortedList.h:

#ifndef _LinkedSortedListClass_
#define _LinkedSortedListClass_

#include "SortedList.h"
#include "LinkedNode.h"

#include <iostream>
#include <fstream>
#include <string>

//using namespace std;

template <class Elem>
class LinkedSortedList : public SortedList< Elem >
{
public:
    LinkedSortedList();
    ~LinkedSortedList();
    virtual void clear();
    virtual bool insert(Elem newValue);
    virtual bool getFirst(Elem &returnValue);
    virtual void print() const;
    virtual bool find(Elem searchValue) const;
    virtual int size() const;
    LinkedNode<Elem>* getHead(); //added for project 2

private:
    LinkedNode<Elem>* head;
    int listSize;
};
#endif

And finally (WHEW!) my Employee.h is here:

#ifndef _EmployeeClass_
#define _EmployeeClass_

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class Employee
{
    public:
        Employee();
        ~Employee();
        Employee(string, string, int);
        bool operator == (Employee& nextEmployee);
        bool operator <= (Employee &nextEmployee);
        bool operator >= (Employee &nextEmployee);
        bool operator < (Employee &nextEmployee);
        bool operator > (Employee &nextEmployee);
        friend ostream& operator<<(ostream& os, const Employee empl);
        string getLastName(){return lastName;}
        string getFirstName(){return firstName;}
        int getEmpID(){return empID;}
    private:
        string lastName;
        string firstName;
        int empID;
};

#endif

I'm just not seeing the problem here, I've spoken to my TA, the helproom staff and other students. I haven't heard back from the prof yet, but I doubt I'll hear from her this weekend anyway. Thank you VERY much for any insight you can provide. I need some tylenol. -Josh

3
  • Change your header guard names. They're reserved. Commented Oct 7, 2012 at 19:30
  • @chris While a good suggestion, I don't think that will solve the linker errors. Commented Oct 7, 2012 at 19:32
  • Neither of these has anything to do with the problem you're seeing, but they're still important. First, the names of the include guards in the header files are reserved to the implementation. Don't use names that contain two underscores or begin with an underscore followed by a capital letter. Second, don't use include guards in source files. Commented Oct 7, 2012 at 19:33

5 Answers 5

18

How are you compiling your code? If you are using an IDE such as Microsoft Visual C++, be sure to create a project and add all of your .cpp files (not your .h files) to it. If you are using the command-line, be sure to include all of your filenames as command-line arguments. If this still doesn't help. Please include a description of the steps you are using to compile the program.

Edit:

There are several issues with your code:

  1. #ifndef ... #define... include guards are meant for files which will appear in #include directives in other files. Typically these are only .h files since it is strongly discouraged to #include .cpp files.

  2. Classes which use templates must all be in one .h file, not separated into a .h and .cpp file. (READ: In case I haven't made this clear enough, your LinkedSortedList class must ALL be in one .h file.)

  3. Your main.cpp file declares several functions which are not later defined. If you don't want to take the time to implement these functions yet, you need to at least add empty stubs for them.

  4. Be sure to compile and link all of your source files using a project in your IDE or using the correct command-line arguments. This is what I described in my original answer above.

Sign up to request clarification or add additional context in comments.

12 Comments

This is true in general case, but first thing you think about when somebody asks about undefined reference in C++ should be "does he use templates and separate declaration from definition?".
@Griwes Good point...although not the first thing I think about ;-)
@user1727170 Ignore points 3. and 4. then. Points 1. and 2. are the important ones to fix your problems.
@user1727170 That might solve your immediate problem, but what if you want LinkedSortedList<int> and LinkedSortedList<double>, too? And what if I take your .obj files and want to create LinkedSortedList<MyVerySpecialClass>? Please read point #2 again.
@Instinct It is because a template doesn't actually define a function or class. It simply gives a recipe for a family of functions or classes. The compiler needs the full template definition in order to instantiate specific versions of the function or class from that recipe.
|
3

To get templates instantiated automatically the compiler needs to the see the definition at the point of use. If you don't want to make the template definition visible you need to use explicit instantiation. How to do either of these is frequently answered.

BTW, please don't use names reserved for the implementation of the C++ compiler and its standard library as include guards: names starting with an underscore followed by a capital letter are reserved in all contexts for the C++ implementation.

Comments

2

My LinkedSortList.cpp is ...

That's the source of most of your problems. Doing what you did doesn't make sense. You need to define those inline in your header file, LinkedSortedList.h. That covers most of your problems the others, where are you defining empPrint(LinkedSortedList<Employee>&)?

1 Comment

That is being defined in my main.cpp. Are you suggesting that I move my LinkedSortedList functions from their .cpp file to the .h file?
1

First of all I cant see definition of these three:-

void empPrint (LinkedSortedList <Employee>& empList);
void empSave (LinkedSortedList <Employee>& empList, string file);
void empLoad (LinkedSortedList <Employee>& empList, string file);

Hope implementing them will reduce the errors.

1 Comment

Those are defined inside the main.cpp, I apparently didn't copy the rest of the code into the question.
1

C++ template methods and functions are not turned into machine code until the compiler runs into a call to the function with a specific type. But that means that the code that defines a template method, for example all of LinkedSortedList.cpp, actually does not produce any code at all when the compiler sees it.

You need to move all of the code from LinkedSortedList.cpp into LinkedSortedList.h (and similarly for the other templated classes) so that the compiler can produce the code for these methods when they are called. For example the compiler will generate the code for LinkedSortedList::LinkedSortedList() when it is called in main.cpp, but only if it's already seen the definition for this method in a header file.

Your other option is to use 'explicit instantiation' to force particular versions of particular methods to be compiled into object code.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.