This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 8 years ago.
Here is my code.
First I defined two class:
#include <iostream>
using namespace std;
template<class Datatype>
class Node
{
public:
Node()
{
next = NULL;
prev = NULL;
}
Node* getNext() const
{
return next;
}
Node* getPrev() const
{
return prev;
}
Datatype* getData() const
{
return &data;
}
void changeNext()
{
next = NULL;
}
void changeNext(Node& nextNode)
{
next = &nextNode;
}
void changePrev()
{
prev = NULL;
}
void changePrev(Node& prevNode)
{
prev = &prevNode;
}
Node* addNext(Node &);
Node* addPrev(Node &);
void nodeDel();
void addData(Datatype &);
private:
Node* next;
Node* prev;
Datatype data;
};
template<class Datatype>
class Stack
{
public:
int push(Datatype &);
Datatype pop();
Datatype* peek();
private:
Node<Datatype> node;
};
This file is called my_node.h. The defines of some functions are in the other file, called my_node.cpp. It is like this:
#include "my_node.h"
using namespace std;
template <class Datatype>
Node<Datatype>* Node<Datatype>::addNext(Node<Datatype>& new_node)
{
if (next == NULL)
{
changeNext(new_node);
new_node.changePrev(*this);
}
else
{
Node* next = getNext();
changeNext(new_node);
new_node.changePrev(*this);
next -> changePrev(new_node);
new_node.changeNext(*next);
}
return &new_node;
}
template <class Datatype>
Node<Datatype>* Node<Datatype>::addPrev(Node<Datatype>& new_node)
{
if (prev == NULL)
{
changePrev(new_node);
new_node.changeNext(*this);
}
else
{
Node* prev = getPrev();
changePrev(new_node);
new_node.changeNext(*this);
prev -> changeNext(new_node);
new_node.changePrev(*prev);
}
return &new_node;
}
template<class Datatype>
void Node<Datatype>::nodeDel()
{
if (prev == NULL && next == NULL)
;
else if (prev == NULL)
{
Node* next = getNext();
next -> changePrev();
}
else if (next == NULL)
{
Node* prev = getPrev();
prev -> changeNext();
}
else
{
Node* next = getNext();
Node* prev = getPrev();
next -> changePrev(*prev);
prev -> changeNext(*next);
}
delete this;
return;
}
template <class Datatype>
void Node<Datatype>::addData(Datatype &new_data)
{
data = new_data;
}
template <class Datatype>
int Stack<Datatype>::push(Datatype &new_data)
{
Node<Datatype> *pt_node = new Node<Datatype>;
if (pt_node == NULL)
return -1;
Datatype *pt_data;
pt_data = (this -> node).getData();
pt_node -> addData(*pt_data);
(this -> node).addData(new_data);
pt_node -> addNext(this -> node);
}
template <class Datatype>
Datatype Stack<Datatype>::pop()
{
Datatype temp((this -> node).data);
Datatype* new_fir = ((this -> node).getNext()) -> getData();
(this -> node).addData(*new_fir);
((this -> node).getNext())->nodeDel();
return temp;
}
template <class Datatype>
Datatype* Stack<Datatype>::peek()
{
return (this->node).getData();
}
So above, I declare class in .h file and define some function of those classes in .cpp file.
Now, I wrote a test file to test how it works. Test file test.cpp is like this:
#include <iostream>
#include "my_node.h"
using namespace std;
int main()
{
Stack<float> test_stack;
float a = 2.3;
float b = 3.4;
test_stack.push(a);
test_stack.push(b);
cout << test_stack.pop();
cout << test_stack.pop();
return 0;
}
The command I use is simply:
g++ -g -Wall my_node.cpp test.cpp -o test
The compiling error is like this:
/tmp/ccYaX0on.o: In function `main':
/home/user/cpp/oop_eg/test.cpp:11: undefined reference to `Stack<float>::push(float&)'
/home/user/cpp/oop_eg/test.cpp:12: undefined reference to `Stack<float>::push(float&)'
/home/user/cpp/oop_eg/test.cpp:14: undefined reference to `Stack<float>::pop()'
/home/user/cpp/oop_eg/test.cpp:15: undefined reference to `Stack<float>::pop()'
collect2: ld returned 1 exit status
I feel so strange because I defined all those functions.
Thanks,
Kevin Zhou
With templates, you generally have to put the whole thing (declaration and definition) into a header file. That's because the code which uses the template needs to see the definition in order to instantiate it.
Basically, you can probably just cut+paste all the functions from my_node.cpp into my_node.h.
Related
I am trying to make a Generic Linked list in C++ using templates. But i am getting this error 'GenericNode::{ctor}': constructors not allowed a return type through which i can't possibly know what am i doing wrong?
PS. i have also gone through other posts here on Stack Overflow which says that the error is due to the missing semi-colon after the class definition but i think i don't have a 'missing semi-colon' case. Any help?
Code :
GenericLinkedList.h :
#pragma once
template <typename Datatype>
class GenericNode {
Datatype T;
GenericNode *next;
public:
GenericNode() {}
GenericNode(Datatype T);
};
template<typename Datatype>
void GenericNode<Datatype>::GenericNode(Datatype data) {
T = data;
}
template <typename Datatype>
class GenericLinkedList {
GenericNode *Data;
public:
GenericLinkedList() {
Data = NULL;
}
int isEmpty();
void addDataAtFront(Datatype data);
void addDataAtEnd(Datatype data);
void print();
};
template <typename Datatype>
int GenericLinkedList<Datatype>::isEmpty() {
return Data == NULL;
}
template <typename Datatype>
void GenericLinkedList<Datatype>::addDataAtFront(Datatype data) {
GenericNode *newNode, *tmpNode;
newNode = new Node;
newNode->T = data;
newNode->next = NULL;
if (Data == NULL) {
Data = newNode;
}
else {
tmpNode = Data;
Data = newNode;
Data->next = tmpNode;
}
}
template <typename Datatype>
void GenericLinkedList<Datatype>::addDataAtEnd(Datatype data) {
GenericNode *newNode, *tmpNode;
newNode = new Node;
newNode->T = data;
newNode->next = NULL;
if (Data == NULL) {
Data = newNode;
}
else {
tmpNode = Data;
while (tmpNode->next != NULL) {
tmpNode = tmpNode->next;
}
tmpNode->next = newNode;
}
}
template <typename Datatype>
void GenericLinkedList<Datatype>::print() {
GenericNode tmpNode;
tmpNode = Data;
for (tmpNode;tmpNode != NULL;tmpNode = tmpNode->next) {
cout << tmpNode->T << " ";
}
}
.cpp :
#include <iostream>
#include <conio.h>
#include "GenericLinkedList.h"
using namespace std;
int main() {
GenericLinkedList<int> T;
T.addDataAtFront(5);
T.addDataAtEnd(6);
T.addDataAtFront(4);
T.print();
_getch();
}
template<typename Datatype>
void GenericNode<Datatype>::GenericNode(Datatype data) {
T = data;
}
You write the return type void. It's a constructor.
void GenericNode::GenericNode(Datatype data)
remove void its a constructor. Constructors don't return and dont have a return type.
I have a problem with creating base class for DoubleLinkedList.
Right now it's giving me this error
/tmp/cc3lORia.o:(.rodata._ZTV24AbstractDoubleLinkedListIiE[_ZTV24AbstractDoubleLinkedListIiE]+0x10):
undefined reference to
`AbstractDoubleLinkedList::createNewNode(int)' collect2: error:
ld returned 1 exit status
I've tried this and that as you can see by commented lines in code, but none of it works.
So how to define abstract template class with abstract method (factory method by the way) and then redefine it in children classes?
/*
* AbstractDoubleLinkedList.hpp
*
* Created on: Mar 2, 2015
* Author: michael
*/
#ifndef ABSTRACTDOUBLELINKEDLIST_H_
#define ABSTRACTDOUBLELINKEDLIST_H_
#include <vector>
using namespace std;
template <class T> class ListNode {
private:
void init();
public:
ListNode();
ListNode(T value);
ListNode *previous;
ListNode *next;
T value;
};
template <class T> void ListNode<T>::init() {
previous = nullptr;
next = nullptr;
}
template <class T> ListNode<T>::ListNode() {
init();
}
template <class T> ListNode<T>::ListNode(T value) {
init();
this->value = value;
}
template <class T> class AbstractDoubleLinkedList {
private:
void pullOutNode(ListNode<T> *node);
protected:
virtual ListNode<T>* createNewNode(T element);
public:
AbstractDoubleLinkedList();
void push_back(T element);
T front();
T back();
void insertBefore(ListNode<T> *node, ListNode<T> *beforeNode);
void insertAfter(ListNode<T> *node, ListNode<T> *afterNode);
void moveNodeAfter(ListNode<T> *node, ListNode<T> *afterNode);
vector<T> toVector();
ListNode<T> *frontNode;
ListNode<T> *backNode;
};
template <class T> void AbstractDoubleLinkedList<T>::push_back(T element) {
ListNode<T>* node = createNewNode(element);
node->previous = backNode;
if (backNode != nullptr) {
backNode->next = node;
node->previous = backNode;
}
else {
frontNode = node;
}
backNode = node;
}
template <class T> void AbstractDoubleLinkedList<T>::pullOutNode(ListNode<T> *node) {
if (node != frontNode) {
node->previous->next = node->next;
}
else {
frontNode = node->next;
}
if (node != backNode) {
node->next->previous = node->previous;
}
else {
backNode = node->previous;
}
}
template <class T> T AbstractDoubleLinkedList<T>::front() {
return frontNode->value;
}
template <class T> T AbstractDoubleLinkedList<T>::back() {
return backNode->value;
}
template <class T> void AbstractDoubleLinkedList<T>::insertAfter(ListNode<T> *node, ListNode<T> *afterNode) {
node->previous = afterNode;
node->next = afterNode->next;
afterNode->next = node;
if (afterNode == backNode) {
backNode = node;
}
}
template <class T> void AbstractDoubleLinkedList<T>::insertBefore(ListNode<T> *node, ListNode<T> *beforeNode) {
node->next = beforeNode;
beforeNode->previous->next = node;
beforeNode->previous = node;
if (beforeNode == frontNode) {
frontNode = node;
}
}
template <class T> void AbstractDoubleLinkedList<T>::moveNodeAfter(ListNode<T> *node, ListNode<T> *afterNode) {
pullOutNode(node);
node->previous = afterNode;
node->next = afterNode->next;
if (node->next == nullptr) {
backNode = node;
}
afterNode->next = node;
}
template <class T> vector<T> AbstractDoubleLinkedList<T>::toVector() {
ListNode<T>* node = frontNode;
vector<int> listAsVector;
bool shouldHaveAnother = (frontNode != nullptr);
while(shouldHaveAnother) {
listAsVector.push_back(node->value);
if (node->next != nullptr)
node = node->next;
else {
shouldHaveAnother = false;
}
}
return listAsVector;
}
template <class T> AbstractDoubleLinkedList<T>::AbstractDoubleLinkedList() {
frontNode = nullptr;
backNode = nullptr;
}
#endif /* ABSTRACTDOUBLELINKEDLIST_HPP_ */
/*
* DoubleLinkedList.hpp
*
* Created on: Feb 26, 2015
* Author: michael
*/
#ifndef DOUBLELINKEDLIST_HPP_
#define DOUBLELINKEDLIST_HPP_
#include "AbstractDoubleLinkedList.hpp"
template <class T> class DoubleLinkedList : public AbstractDoubleLinkedList<T> {
protected:
ListNode<T>* createNewNode(T element) {
return new ListNode<T>(element);
}
public:
~DoubleLinkedList() {
ListNode<T>* node = this->backNode;
bool shouldHaveAnother = (node != nullptr);
while(shouldHaveAnother) {
ListNode<T>* ptr = node->previous;
delete node;
if (ptr != nullptr)
node = ptr;
else {
shouldHaveAnother = false;
}
}
};
};
//template <class T> ListNode<T> DoubleLinkedList<T>::createNewNode(T element) {
// return new ListNode<T>(element);
//}
//template <class T> DoubleLinkedList<T>::~DoubleLinkedList() {
//
// ListNode<T>* node = this->backNode;
// bool shouldHaveAnother = (node != nullptr);
// while(shouldHaveAnother) {
// ListNode<T>* ptr = node->previous;
// delete node;
// if (ptr != nullptr)
// node = ptr;
// else {
// shouldHaveAnother = false;
// }
// }
//}
#endif /* DOUBLELINKEDLIST_HPP_ */
EDIT1:
So, there is a question "Why do I need my own container class and why not use list or vector?"
I need a data structure with constant time random access and constant time deletion and insertion. BUT I've already tried std::unordered_set and It's not good enough (while technically it meets the requirements) because of allocations and deallocations of memory (when deleting and inserting)
So I figured another way. I want to use a linked list with one "guard" element and the end. When I need to "delete" element from it I would move it after guard. And to check if list is "empty" I would check if the first element is the guard element. But I need a constant-time random access. This can be achieved by map of pointers to every element.
But to achieve maximum performance I need to also minimize cache misses. And I thinks that std::list would be scattered across the memory, because it is the normal behaviour for it. So I figured that the only way to do so - is to allocate vector> and then use this preallocated nodes to new elements.
So am I wrong somewhere? Can I achieve maximum performance more easier?
This question already has answers here:
Why do I get "unresolved external symbol" errors when using templates? [duplicate]
(3 answers)
Undefined reference to template members
(1 answer)
Closed 10 years ago.
I always get
undefined reference to `Graph::InsertVertex(std::string)'
if I compile my project! Any hints why he cant resolve this reference?
(all Files are in the netbeans project folder)
// main.cpp
#include <cstdlib>
#include <string>
#include "Graph.h"
using namespace std;
int main(int argc, char** argv)
{
Graph<string> *graph = new Graph<string>(); // <--- ERROR
graph->InsertVertex("A");
return 0;
}
// Node.h
#include <iostream>
#include "Graph.h"
template<class T>
class Node
{
friend class Graph;
public:
Node(T val)
{
this->data = val;
this->vertList = NULL;
this->next = NULL;
}
Node(const Node& orig);
virtual ~Node();
private:
T data;
Node<T> *vertList;
Node<T> *next;
int status;
};
// Graph.h
#include <iostream>
#include "Node.h"
template <class T>
class Graph
{
public:
Graph()
{
head = NULL;
}
void InsertVertex(T val);
void InsertEdge(T v_val, T e_val);
void PrintVertices();
void PrintEdges(T v_val);
void DeleteEdge(T v_val, T e_val);
void DeleteVertex(T val);
void bfs();
private:
Node<T> *head;
};
// Graph.cpp
#include "Graph.h"
template <class T>
void Graph<T>::InsertVertex(T val)
{
Node<T> *temp = new Node<T>(val);
if(head == NULL) head = temp;
else
{
Node<T> node = head;
while(node->vertList != NULL)
node = node->vertList;
node->vertList = temp;
}
}
template <class T>
void Graph<T>::InsertEdge(T v_val, T e_val)
{
if (head != NULL)
{
Node<T> *k = head;
Node<T> *t = head;
Node<T> *temp = new Node<T> (e_val);
while (t != NULL)
{
if (t->data == v_val)
{
Node<T> *s = t;
while (s->next != NULL)
s = s->next;
s->next = temp;
while (k != NULL)
{
if(k->data == e_val) break;
k = k->vertList;
}
temp->vertList = k;
return;
}
t = t->vertList;
} // end while loop
}
else std::cout << "Add first vertices to the graph" << std::endl;
}
template <class T>
void Graph<T>::PrintEdges(T v_val)
{
Node<T>* t = head;
while (t != NULL)
{
if (t->data == v_val)
{
while (t->next != NULL)
{
std::cout << t->next->vertList->data << " ";
t = t->next;
}
}
t = t->vertList;
}
}
template <class T>
void Graph<T>::PrintVertices()
{
Node<T>* t = head;
while (t != NULL)
{
std::cout << t->data << " ";
t = t->vertList;
}
}
Typically you want your template methods in the header, so they are compiled when needed. In case you really want to hide it in the implementation file, you have to explicitly instantiate the template in Graph.cpp like
template class Graph<string>;
Since you have to do that for every type T you intend to use with Graph<T>, the point of the template class is somewhat defeated and you better put everything into the header
You need to define member functions in a header file, because when instantiating a template, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument.
In your example:
template <class T>
class Graph {
public:
void InsertVertex(T val) {
Node<T> *temp = new Node<T>(val);
if(head == NULL)
head = temp;
// ...
}
// ...
private:
Node<T> *head;
};
So I wrote a Linked List based implementation of the Stack ADT recently. However, I'm not quite sure why there is a bit of a discrepancy between how the nodes of the Stack are being declared. The compiler gets very angry and won't compile until I write them a certain way for certain functions. I'm extremely curious as to why this is the case.
Here are two different methods which the compiler wants two different formats.
Here is my destructor where the compiler wants StackNode *temp.
template <typename DataType>
StackLinked<DataType>::~StackLinked() {
StackNode *temp;
while (top != 0) {
temp = top;
top = top->next;
delete temp;
}
}
Here is my assignment operator overload where the compiler wants StackNode<DataType> *temp.
template <typename DataType>
StackLinked<DataType>& StackLinked<DataType>::operator=(const StackLinked& other) {
if (this != &other) {
StackNode<DataType> *newNode, *current, *last;
if (top != 0) {
StackNode<DataType> *temp;
while (top != 0) {
temp = top;
top -> top->next;
delete temp;
}
}
if (other.top == 0) {
top = 0;
}
else {
current = other.top;
top = new StackNode<DataType>;
top->dataItem = current->dataItem;
top->next = 0;
last = top;
current = current->next;
while (current != 0) {
newNode = new StackNode<DataType>;
newNode->dataItem = current->dataItem;
newNode->next = 0;
last-> next = newNode;
last = newNode;
current = current->next;
}
}
}
return *this;
}
I don't know why this is, but the unknown is bothering me.
Note: My StackNode class is an inner class of the StackLinked class.
EDIT: Class declaration:
#ifndef STACKARRAY_H
#define STACKARRAY_H
#include <stdexcept>
#include <iostream>
using namespace std;
#include "Stack.h"
template <typename DataType>
class StackLinked : public Stack<DataType> {
public:
StackLinked(int maxNumber = Stack<DataType>::MAX_STACK_SIZE);
StackLinked(const StackLinked& other);
StackLinked& operator=(const StackLinked& other);
~StackLinked();
void push(const DataType& newDataItem) throw (logic_error);
DataType pop() throw (logic_error);
void clear();
bool isEmpty() const;
bool isFull() const;
void showStructure() const;
private:
class StackNode {
public:
StackNode(const DataType& nodeData, StackNode* nextPtr);
DataType dataItem;
StackNode* next;
};
StackNode* top;
};
#endif
If any other details are needed. Just ask! Thank you for your time!
From the code you showed, StackNode<DataType> is not correct, because StackNode is not a class template.
This makes me think you have a template also named StackNode that the compiler is finding. Go check whether any of your files contain another version of StackNode.
I have three files and I want to compile and run them, but I keep getting some errors and warnings. Redefinition of struct Node< T >. I don't know much about templates, but this looks right to me. And, I spent a lot of time trying to figure out whats wrong. Thanks.
//mystack.h
#ifndef MYSTACK_H
#define MYSTACK_H
template <class T>
struct Node
{
T info;
T *next;
};
template <class T>
class MyStack
{
private:
struct Node<T> *top;
public:
void Push(T item);
void Pop();
int Top();
void Print();
};
#endif
//mystack.cpp
#include <iostream>
#include "mystack.h"
template <class T>
struct Node
{
T info;
T* next;
};
template <class T>
class MyStack
{
private:
struct Node<T>* top;
public:
void Push(T item)
{
if(top == NULL)
{
top = new( struct Node<T> );
top->info = item;
top->next = NULL;
} else
{
Node<T>* temp;
temp = top;
top = new( struct Node<T> );
top->info = item;
top->next = temp;
}
}
void Pop()
{
if( top == NULL )
{
} else
{
Node<T>* temp;
temp = top->next;
delete top;
top = temp;
}
}
int Top()
{
return top;
}
void Print()
{
if(top != NULL)
{
Node<T>* temp;
temp = top;
while(temp != NULL)
{
std::cout << temp << std::endl;
temp = temp->next;
}
}
}
};
One mistake you did in the listings is that you redefine this structure as it says.
Thats the definition:
template <class T>
struct Node
{
T info;
T* next;
};
This definition is done in both listings.
Edit: The second thing is that your class method implementation does not look right. You will have the most success if you try not to split cpp and header files while using templates.
If you were designing a class instead of a template, what you're doing would be wrong because you're redefining types.
But since you're writing templates, you're wrong earlier than that: you can't separately compile templates.
Brief pointer on the C++ compilation model:
// Definition of Node
template<typename T>
struct Node {
T info;
T* next; // shouldn't that be a Node*?
};
// Definition of MyStack
template <typename T>
class MyStack
{
private:
Node<T> *top;
public:
// Declarations, but not definitions, of the Mystack function members.
void Push(T item);
void Pop();
int Top();
void Print();
};
// Example definition of MyStack::Push
template<typename T>
void
MyStack<T>::Push(T item)
{
// as before
}
The type definitions usually appear in headers (if they are to be reused in different TUs) with include guards as you are doing. The guards are here because the definitions must appear at most once per TU. Do not manually repeat a type definition (e.g. in a source file as you did). This is wrong as it should be: nobody wants copy-n-paste errors.
The function members definitions usually appear in source files, unless they are the members of a template. In the latter case it's simpler to put them in headers (they don't have to be inline either).
You can learn the details of the compilation model elsewhere on SO, or in books, or on the Internet. Searching for 'template definition' or 'one definition rule' (or ODR) can help.
First, In header, remove 'struct' from the line 'struct Node *top;'. In C++ structs are almost identical to classes, the only difference being that struct members are public by default and class members are private by default. You do not need to preface struct types with the struct keyword like in straight C.
Second, Your CPP is all wrong. Templates are instantiated by the compiler when needed, so they do not live in CPP files to be compiled into objects like normal (apart from template specializations). You can put your template definitions in the HPP itself or a better general solution is to use an IPP file, i.e
// mystack.ipp
#ifndef MYSTACK_IPP
#define MYSTACK_IPP
#include "mystack.h"
#include <iostream>
template <class T>
void MyStack<T>::Push(T item)
{
if(top == NULL)
{
top = new( struct Node<T> );
top->info = item;
top->next = NULL;
} else
{
Node<T>* temp;
temp = top;
top = new( struct Node<T> );
top->info = item;
top->next = temp;
}
}
template <class T>
void MyStack<T>::Pop()
{
if( top == NULL )
{
} else
{
Node<T>* temp;
temp = top->next;
delete top;
top = temp;
}
}
template <class T>
int MyStack<T>::Top()
{
return top;
}
template <class T>
void MyStack<T>::Print()
{
if(top != NULL)
{
Node<T>* temp;
temp = top;
while(temp != NULL)
{
std::cout << temp << std::endl;
temp = temp->next;
}
}
}
#endif
Then '#include "mystack.ipp"' in any file which makes use of the implementation of MyStack