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;
}
}
Does anyone know what the issue is?