0

In one of my projects, I have tried to implement a template for a Doubly Linked List, as shown below.

DoubleLinkedList.h

#pragma once

template <class T>
struct Node {
    T* item;
    Node* next;
    Node* prev;

    Node(T* newItem, Node* newNext, Node* newPrev);
    Node();
    ~Node();
};

template <class T>
class DoubleLinkedList {
    Node<T>* headNode;
    Node<T>* nodePointer;
    int count;

public:
    DoubleLinkedList(T* firstItem);
    DoubleLinkedList();
    ~DoubleLinkedList();

    /*
    Adds a new node with the newItem to the list to just next of where the nodePointer is at. If nodePointer is NULL, adds it to headNode.
    nodePointer points to the newly added node.
    */
    void addItem(T* newItem);

    /*
    Removes the node that nodePointer is pointing to. If nodePointer is NULL, does nothing. nodePointer moves to the next node after removal.
    */
    void removeItem();

    /*
    Get the item from the node that nodePointer is pointing at.
    */
    T* getItem();

    /*
    Get the item from the node referenced by index. (e.g. index = 0 means the item in the headNode)
    */
    T* getItem(int index);

    /*
    Move the pointer to the node indicated by index.
    */
    void setPointerIndex(int index);

    /*
    Move the pointer to the next node in the list. If the pointer is already at the end of the list, does nothing.
    */
    void pointerNext();
    
    /*
    Move the pointer to the previous node in the list. If the pointer is already at the end of the list, does nothing.
    */
    void pointerPrev();

    /*
    Moves the pointer to the headNode
    */
    void movePointerToHead();

    /*
    Moves the pointer to the end of the list
    */
    void movePointerToEnd();

    void setHeadNode(Node<T>* newHead);
    void setNodePointer(Node<T>* newPointer);
    void setCount(Node<T>* newCount);
    Node<T>* getHead();
    Node<T>* getPointer();
    int getCount();
};

DoubleLinkedList.cpp

#include "DoubleLinkedList.h"

template<class T>
Node<T>::Node(T* newItem, Node* newNext, Node* newPrev)
{
    item = newItem;
    next = newNext;
    prev = newPrev;
}

template<class T>
Node<T>::Node()
{
}

template<class T>
Node<T>::~Node()
{
    delete item;
}

template<class T>
DoubleLinkedList<T>::DoubleLinkedList(T* firstItem)
{
    Node<T>* firstNode = new Node<T>();
    firstNode->item = firstItem;
    headNode = firstNode;
    nodePointer = headNode;
    count = 1;
}

template<class T>
DoubleLinkedList<T>::DoubleLinkedList()
{
    headNode = nullptr;
    nodePointer = nullptr;
    count = 0;
}

template<class T>
DoubleLinkedList<T>::~DoubleLinkedList()
{

}

template<class T>
void DoubleLinkedList<T>::addItem(T* newItem)
{
    Node<T> toAdd = new Node<T>();
    toAdd->item = newItem;
    if (nodePointer != nullptr) {
        headNode = toAdd;
    }
    else {
        toAdd->prev = nodePointer;
        toAdd->next = nodePointer->next;
        nodePointer->next = toAdd;
        nodePointer = toAdd->next;
        nodePointer->prev = toAdd;
    }
    nodePointer = toAdd;
    count++;
}

template<class T>
void DoubleLinkedList<T>::removeItem()
{
    if (nodePointer != nullptr) {
        if (nodePointer == headNode) {
            headNode = nodePointer->next;
            delete nodePointer;
            nodePointer = headNode;
        }
        else {
            if (nodePointer->prev != nullptr && nodePointer->next != nullptr) {
                Node<T> prevNode = nodePointer->prev;
                Node<T> nextNode = nodePointer->next;
                prevNode->next = nextNode;
                nextNode->prev = prevNode;
            }
            Node<T>* toDelete = nodePointer;
            nodePointer = nodePointer->next;
            if (nodePointer == nullptr)
                nodePointer = headNode;
            delete toDelete;
        }
        count--;
    }
}

template<class T>
T* DoubleLinkedList<T>::getItem()
{
    return nodePointer->item;
}

template<class T>
T* DoubleLinkedList<T>::getItem(int index)
{
    return nullptr;
}

template<class T>
void DoubleLinkedList<T>::setPointerIndex(int index)
{
    movePointerToHead();
    for (int i = 0; i < index; i++)
    {
        pointerNext();
    }
}

template<class T>
void DoubleLinkedList<T>::pointerNext()
{
    if (nodePointer->next != nullptr)
    {
        nodePointer = nodePointer->next;
    }
}

template<class T>
void DoubleLinkedList<T>::pointerPrev()
{
    if (nodePointer->prev != nullptr)
    {
        nodePointer = nodePointer->prev;
    }
}

template<class T>
void DoubleLinkedList<T>::movePointerToHead()
{
    nodePointer = headNode;
}

template<class T>
void DoubleLinkedList<T>::movePointerToEnd()
{
    movePointerToHead();
    while (nodePointer->next != nullptr)
    {
        pointerNext();
    }
}

template<class T>
void DoubleLinkedList<T>::setHeadNode(Node<T>* newHead)
{
    headNode = newHead;
}

template<class T>
void DoubleLinkedList<T>::setNodePointer(Node<T>* newPointer)
{
    nodePointer = newPointer;
}

template<class T>
void DoubleLinkedList<T>::setCount(Node<T>* newCount)
{
    count = newCount;
}

template<class T>
Node<T>* DoubleLinkedList<T>::getHead()
{
    return headNode;
}

template<class T>
Node<T>* DoubleLinkedList<T>::getPointer()
{
    return nodePointer;
}

template<class T>
int DoubleLinkedList<T>::getCount()
{
    return count;
}

Now, I have a bit of code where I try to create an instance of a DoubleLinkedList for a class I made called Score in another file Scoreboard.cpp. The details of score are not important, but when I tried to compile it, I received a bunch of LNK2019 errors that all say that it cannot find the definition of the functions that I call.

Scoreboard.h

#pragma once

#include "DoubleLinkedList.h"
#include "Score.h"

class Scoreboard {
    DoubleLinkedList<Score>* scoreList;
    int scoreCount;

public:
    Scoreboard();
    ~Scoreboard();

    // Adds a new score
    void addScore(Score* score);

    // Adds a new score
    void addScore(int correct, int total, double time, int type, std::string name = NULL);

    // Clear scoreboard
    void clearScoreboard();

    // Prints the contents of the scoreboard onto the output stream
    void print();

    // Serializes the scoreboard onto a file.
    void serialize();

    // Deserializes the scoreboard from an existing file
    void deserialize();

    // Export the scoreboard as a readable text file.
    void exportScoreboard();

    void setScoreList(DoubleLinkedList<Score> newScoreList);
    void setScoreCount(int newScore);
    DoubleLinkedList<Score>* getScoreList();
    int getScoreCount();
};

Scoreboard.cpp

#include "Scoreboard.h"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <ratio>

Scoreboard::Scoreboard()
{
    scoreList = new DoubleLinkedList<Score>();
    scoreCount = 0;
}

Scoreboard::~Scoreboard()
{
    delete scoreList;
}

void Scoreboard::addScore(Score* score)
{
    scoreList->addItem(score);
}

void Scoreboard::addScore(int correct, int total, double time, int type, std::string name)
{
    Score* toAdd = new Score(correct, total, time, type, name);
    scoreList->addItem(toAdd);
}

void Scoreboard::clearScoreboard()
{
    while (scoreList->getCount() > 0)
    {
        scoreList->removeItem();
    }
}

void Scoreboard::print()
{
    std::cout << "           Name";
    std::cout << "   Correct";
    std::cout << "     Time (sec)";
    std::cout << "           Type";
    std::cout << "          Score";
    std::cout << "                 Date and Time";

    scoreList->movePointerToHead();
    for (int i = 0; i < scoreList->getCount(); i++)
    {
        std::cout << std::endl;
        scoreList->getItem()->print();
        scoreList->pointerNext();
    }
}

void Scoreboard::serialize()
{
    std::ofstream scoreboardOutput("scoreboard_data");

    scoreList->movePointerToHead();
    for (int i = 0; i < scoreList->getCount(); i++)
    {
        // Serialize by writing to file seperated by new lines
        Score* toSerialize = scoreList->getItem();
        scoreboardOutput << toSerialize->name.c_str() << "\n";
        scoreboardOutput << toSerialize->correct << "\n" << toSerialize->total << "\n";
        scoreboardOutput << toSerialize->time << "\n";
        switch (toSerialize->type) {
        case 0:
            scoreboardOutput << "Addition\n";
            break;
        case 1:
            scoreboardOutput << "Subtraction\n";
            break;
        case 2:
            scoreboardOutput << "Multiplication\n";
            break;
        case 3:
            scoreboardOutput << "Division\n";
            break;
        default:
            scoreboardOutput << "Unknown\n";
        }
        scoreboardOutput << toSerialize->score << "\n";
        
        // Serialize the date time by converting it into integer values of time since epoch.
        int serializeDateTime = toSerialize->dateTime.time_since_epoch().count();
        scoreboardOutput << serializeDateTime << "\n";

        scoreList->pointerNext();

    }
}

void Scoreboard::exportScoreboard()
{
    std::ofstream scoreboardOutput("scoreboard.txt");
    scoreboardOutput << "           Name";
    scoreboardOutput << "   Correct";
    scoreboardOutput << "     Time (sec)";
    scoreboardOutput << "           Type";
    scoreboardOutput << "          Score";
    scoreboardOutput << "                 Date and Time";

    scoreList->movePointerToHead();
    for (int i = 0; i < scoreList->getCount(); i++)
    {
        scoreboardOutput << std::endl;

        Score* toSerialize = scoreList->getItem();
        scoreboardOutput << std::setw(15) << toSerialize->name.c_str();
        scoreboardOutput << std::setw(10) << toSerialize->correct << "/" << toSerialize->total;
        scoreboardOutput << std::setw(15) << time;
        switch (toSerialize->type) {
        case 0:
            scoreboardOutput << std::setw(15) << "Addition";
            break;
        case 1:
            scoreboardOutput << std::setw(15) << "Subtraction";
            break;
        case 2:
            scoreboardOutput << std::setw(15) << "Multiplication";
            break;
        case 3:
            scoreboardOutput << std::setw(15) << "Division";
            break;
        default:
            scoreboardOutput << std::setw(15) << "Unknown";
        }
        scoreboardOutput << std::setw(15) << toSerialize->score;
        std::time_t tt = std::chrono::system_clock::to_time_t(toSerialize->dateTime);
        char timeString[30];
        ctime_s(timeString, sizeof(timeString), &tt);
        scoreboardOutput << std::setw(30) << timeString;
    }
}

LNK2019 Errors on VS 2022

Does anyone know what the issue is?

  • 1
    *"I received a bunch of LNK2019 errors"* -- a better question would reduce this down to *one* error (or at least fewer errors). The first error in your image (which should have been posted as text, not an image) is for the default constructor of `DoubleLinkedList`. Go with that. Take everything else out of the template definition, leaving you with just `template class DoubleLinkedList { public: DoubleLinkedList(); };`. This lets you strip out a bunch of other code. For example, you could also reduce `Scoreboard` to just a constructor. This is the "minimal" part of [mre]. – JaMiT May 29 '22 at 23:16
  • `addItem` checks `nodePointer` and if it is `nullptr` then you access `nodePointer->next`. See anything wrong there? – Goswin von Brederlow May 29 '22 at 23:24
  • Tip: If you make `nodePointer` a `Node **nodePointer = &headNode;` then you don't need a special case for adding to the empty list. – Goswin von Brederlow May 29 '22 at 23:25
  • Tip2: use `std::unique_ptr` to pass items to add to the list to clearly express the list takes ownership. – Goswin von Brederlow May 29 '22 at 23:26

0 Answers0