I’m receiving the infamous “undefined reference” error when trying to compile & link several files and would be glad if you could help.
The exact error message:
g++ -o main list.cpp main.cpp
/tmp/ccv6M2I6.o: In functionmain':List::print() const’
main.cpp:(.text+0x219): undefined reference to
main.cpp:
#include <iostream>
#include "list.hpp"
using namespace std;
void printStats(IntList& l) {
cout << endl << "____________________" << endl;
cout << "Length: " << l.getCount() << endl;
cout << "Min: " << l.min() << endl;
cout << "Max: " << l.max() << endl;
cout << "Average: " << l.average();
cout << endl << "____________________" << endl;
}
int main() {
IntList l = IntList();
for(int i=1; i <= 10; i++) {
l.insert(i, l.getCount() - 1); // works fine
}
printStats(l); // works fine, too
l.print(); // causes the error
return 0;
}
The funny thing is: neither the member function insert() nor min(), max() or average() cause any problems. It’s just print().
[EDIT]: It’s not just print(), but also remove().
list.hpp:
#ifndef __LIST_HPP__
#define __LIST_HPP__
template <typename T>
class List {
public:
class OutOfBoundsException { };
List();
List(const List& l);
~List();
List& operator=(const List& l);
unsigned int getCount() const;
bool isEmpty() const;
void print() const;
void insert(T value, unsigned int position = 0);
void remove(unsigned int position);
T pop(unsigned int position = 0);
T getElement(unsigned int position) const;
protected:
// double linked list
struct dllist_entry {
T value;
dllist_entry* next;
dllist_entry* prev;
};
dllist_entry* first;
dllist_entry* last;
unsigned int length;
void clear();
dllist_entry* getElementRaw(unsigned int position) const;
};
class IntList : public List<int> {
public:
IntList();
IntList(const IntList& l);
~IntList();
IntList& operator=(const IntList& l);
int max() const;
int min() const;
float average() const;
};
#endif
list.cpp:
#include <iostream>
#include "list.hpp"
using namespace std;
template <typename T>
List<T>::List() {
this->first = NULL;
this->last = NULL;
this->length = 0;
}
template <typename T>
List<T>::List(const List& l) {
this->first = NULL;
this->last = NULL;
this->length = 0;
for(unsigned int i=0; i < l.getCount(); i++) {
insert(l.getElement(i));
}
}
template <typename T>
List<T>& List<T>::operator=(const List<T>& l) {
if(this != &l) {
// Liste leeren
clear();
for(unsigned int i=0; i < l.getCount(); i++) {
insert(l.getElement(i));
}
}
return *this;
}
template <typename T>
List<T>::~List() {
clear();
}
template <typename T>
void List<T>::clear() {
dllist_entry* iter = first;
dllist_entry* next;
while(iter != NULL) {
next = iter->next;
delete iter;
iter = next;
}
length = 0;
}
template <typename T>
unsigned int List<T>::getCount() const {
return this->length;
}
template <typename T>
bool List<T>::isEmpty() const {
return this->length == 0;
}
template <typename T>
void List<T>::print() const {
// aus Performance-Gründen nicht getElement() benutzen
for(dllist_entry* iter = first; iter != NULL; iter = iter->next) {
cout << iter->value << endl;
}
}
template <typename T>
void List<T>::insert(T value, unsigned int position) {
dllist_entry* new_one = new dllist_entry;
new_one->value = value;
if(getCount() > 0) {
if(position < getCount()) {
if(position == 0) {
new_one->prev = NULL;
new_one->next = first;
first->prev = new_one;
first = new_one;
}
// position > 0
else {
dllist_entry* elem = getElementRaw(position);
new_one->next = elem;
new_one->prev = elem->prev;
elem->prev->next = new_one;
elem->prev = new_one;
}
}
else if(position == getCount()) {
new_one->next = NULL;
new_one->prev = last;
last->next = new_one;
last = new_one;
}
else {
throw OutOfBoundsException();
}
}
else {
new_one->next = NULL;
new_one->prev = NULL;
first = new_one;
last = new_one;
}
length++;
}
template <typename T>
T List<T>::pop(unsigned int position) {
T value = getElement(position);
remove(position);
return value;
}
template <typename T>
void List<T>::remove(unsigned int position) {
dllist_entry* elem = getElementRaw(position);
if(getCount() == 1) { // entspricht elem == first && elem == last
first = NULL;
last = NULL;
}
else if(elem == first) {
elem->next->prev = NULL;
first = elem->next;
}
else if(elem == last) {
elem->prev->next = NULL;
last = elem->prev;
}
// Element liegt zwischen Anfang und Ende
// (Wäre das nicht so, hätte getElementRaw() bereits protestiert.)
else {
elem->prev->next = elem->next;
elem->next->prev = elem->prev;
}
delete elem;
length--;
}
template <typename T>
T List<T>::getElement(unsigned int position) const {
return getElementRaw(position)->value;
}
template <typename T>
typename List<T>::dllist_entry* List<T>::getElementRaw(unsigned int position) const {
// schließt den Fall getCount() == 0 mit ein
if(position < getCount()) {
dllist_entry* iter;
// aus Performance-Gründen mit der Suche entweder von vorne oder
// von hinten beginnen
if(position <= (getCount() - 1) / 2) {
iter = first;
for(unsigned int i=0; i < position; i++) {
iter = iter->next;
}
}
else {
iter = last;
for(unsigned int i = getCount() - 1 ; i > position; i--) {
iter = iter->prev;
}
}
return iter;
}
else {
throw OutOfBoundsException();
}
}
IntList::IntList() : List<int>() { }
IntList::IntList(const IntList& l) : List<int>(l) { }
IntList::~IntList() { }
IntList& IntList::operator=(const IntList& l) {
List<int>::operator=(l);
return *this;
}
int IntList::min() const {
// erstes Element separat holen, damit OutOfBoundsException geworfen werden
// kann, wenn Liste leer ist
int min = getElement(0);
for(unsigned int i=1; i < getCount(); i++) {
int value = getElement(i);
if(value < min) {
min = value;
}
}
return min;
}
int IntList::max() const {
// erstes Element separat holen, damit OutOfBoundsException geworfen werden
// kann, wenn Liste leer ist
int max = getElement(0);
for(unsigned int i=1; i < getCount(); i++) {
int value = getElement(i);
if(value > max) {
max = value;
}
}
return max;
}
float IntList::average() const {
if(getCount() > 0) {
int sum = 0;
for(unsigned int i=0; i < getCount(); i++) {
sum += getElement(i);
}
return (float) sum / getCount();
}
else {
return 0;
}
}
Sorry for the large source but I was afraid I could accidentally leave something out if I only posted extracts.
For the record: I received a similar error message – this time with List::~List() – before I explicitely declared / defined the destructor ~IntList() in list.hpp / list.cpp. I actually expected I shouldn’t even need to declare it since the destructor of the parent class List is called anyway when destroying an IntList object? Also, even defining the destructor directly in the header file list.hpp as “~IntList() { }” didn’t do the trick – the error message wouldn’t disappear until I moved the dtor definition to list.cpp.
On a side note: The whole thing compiled wonderfully when I had it still in one big file.
Thanks for taking the time hunting this bug down! 🙂
The definition of template should be in same file in which declaration is provided. They cannot be separated.
So either you move all the definitons of
list.cpptolist.hpp, Or do this inlist.hppAnd remove the line
#include list.hppfromlist.cppfile. It is making it circular:As a side note, done use
using namespace std. Use fully-qualified name, such as: