未定义对 (函数) 的引用 c++。

12

可能重复:
Undefined Reference to

我已经花了大约4个小时来解决这一组错误信息,但似乎无法弄清楚。我之前没有在这里发过帖子,所以如果我不在正确的地方或者做错了什么,请提前谅解。无论如何,我收到的错误信息是:

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()'|

我的main.cpp如下:

#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;
        }
    }
}

我的 LinkedSortList.cpp 如下:

#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

Employee.cpp是:

#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

我的头文件,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

最后(终于)我的Employee.h文件来了:

#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

我不明白问题出在哪里,我已经和我的TA、帮助室工作人员以及其他学生交流过了。我还没有收到教授的回复,但我觉得这个周末她也不会回复我。非常感谢您提供的任何见解。我需要一些泰诺。


更改您的头文件保护名称。它们是保留的 - chris
@chris 虽然是个好建议,但我认为那并不能解决链接器错误。 - Code-Apprentice
这两个问题都与你所遇到的问题无关,但它们仍然很重要。首先,在头文件中使用的包含保护符名称是保留给实现的。不要使用包含两个下划线或以下划线后跟大写字母的名称。其次,不要在源文件中使用包含保护符。 - Pete Becker
5个回答

18
你是如何编译你的代码的?如果你使用像Microsoft Visual C++这样的IDE,请确保创建一个项目并将所有的.cpp文件(而不是.h文件)添加到其中。如果你使用命令行,请确保将所有的文件名作为命令行参数包含在内。如果这仍然没有帮助,请描述一下你用来编译程序的步骤。
编辑:
你的代码存在几个问题:
1. #ifndef ... #define... include guards适用于将出现在其他文件的#include指令中的文件。通常这些只是.h文件,因为强烈不建议#include.cpp文件。
2. 使用模板的类必须全部放在一个.h文件中,而不是分成.h和.cpp文件。(阅读:如果我没有表达清楚,你的LinkedSortedList类必须全部放在一个.h文件中。)
3. 你的main.cpp文件声明了几个函数,但后面没有定义。如果你不想花时间实现这些函数,你至少需要添加空的存根。
4. 确保使用IDE中的项目或使用正确的命令行参数编译和链接所有的源文件。这就是我在上面的原始答案中所描述的。

4
通常情况下这是正确的,但当有人询问C++中未定义引用时,你首先需要考虑的是“他是否使用了模板,并将声明与定义分开?”。 - Griwes
1
@user1727170 忽略第三点和第四点。第一点和第二点是解决您问题的重要步骤。 - Code-Apprentice
1
@user1727170 这可能解决您的直接问题,但如果您也想要 LinkedSortedList<int>LinkedSortedList<double> 呢?如果我拿走您的 .obj 文件并想要创建 LinkedSortedList<MyVerySpecialClass> 呢?请再次阅读第二点。 - Code-Apprentice
@Code-Guru 这个答案对我解决使用模板链表和模板节点时遇到的问题非常有帮助!为什么模板类不能将声明/实现分开呢?(在主函数中尝试实例化一个新的模板链表时出现未定义错误,现在可以正常工作了) - Instinct
2
@Instinct 这是因为模板实际上并没有定义函数或类。它只是提供了一组函数或类的配方。编译器需要完整的模板定义来从该配方实例化函数或类的特定版本。 - Code-Apprentice
显示剩余7条评论

3
为了让模板能够自动实例化,编译器需要在使用的地方看到定义。如果您不想让模板定义可见,您需要使用显式实例化。如何执行其中任何一个通常都有答案。
顺便说一句,请不要使用保留给C++编译器及其标准库实现作为包含保护符号的名称:以下划线开头后跟大写字母的名称在所有情况下都为C++实现保留。

2

我的 LinkedSortList.cpp 是...

这是你大部分问题的源头。你所做的事情没有意义。你需要在你的头文件 LinkedSortedList.h 中定义那些 内联(inline) 函数。这样可以解决你的大部分问题,其他问题是,你在哪里定义了 empPrint(LinkedSortedList<Employee>&)


1
这是在我的main.cpp中定义的。您是否建议我将LinkedSortedList函数从它们的.cpp文件移动到.h文件中? - JoshH

1

首先,我看不到这三个的定义:

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

希望实施它们能够减少错误。

这些定义在main.cpp中,显然我没有将其余的代码复制到问题中。 - JoshH

1

C++模板方法和函数直到编译器遇到具有特定类型的函数调用时才转换为机器代码。但这意味着定义模板方法的代码,例如LinkedSortedList.cpp中的所有代码,在编译器看到它时实际上不会产生任何代码。

您需要将LinkedSortedList.cpp中的所有代码移动到LinkedSortedList.h中(以及其他模板类的类似操作),以便编译器在调用这些方法时可以生成代码。例如,当在main.cpp中调用LinkedSortedList::LinkedSortedList()时,编译器将生成该方法的代码,但前提是它已经在头文件中看到了该方法的定义。

您的另一个选择是使用“显式实例化”来强制编译特定版本的特定方法成为目标代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接