-1

In the program below, I'm creating an object with one string and one linkedlist<int>.

I keep getting an unresolved issue due to the obj in main(). The error says:

error LNK2019: unresolved external symbol "public: __thiscall List::List(class List const &)" (??0?$List@H@@QAE@ABV0@@Z) referenced in function "public: __thiscall Data::Data(class Data const &)" (??0Data@@QAE@ABV0@@Z)

      main.cpp
            #include "List.h"
            #include <string>
            #include <iostream>
            #include <fstream>
            #include <utility> 
            using namespace std;

            template <class Object>
            void printList( const List<Object> & theList )  // SIMPLE PRINT FUNCTION
            {
                if( theList.isEmpty( ) )
                    cout << "Empty list" << endl;
                else
                {
                    ListItr<Object> itr = theList.first( );
                    for( ; !itr.isPastEnd( ); itr.advance( ) )
                        cout << itr.retrieve( ) << " ";
                }

                cout << endl;
            }

            class Data
            {
            public:

                Data(){
                    keyword = "";
                    List<int> pages;
                }

                void addNewData(string key, int article){

                    ListItr<int> pageitr = pages.zeroth();
                    keyword = key;
                    pages.insert(article, pageitr);

                }

                void addNewPage(int article){

                    ListItr<int> pageitr = pages.zeroth();
                    if(!pages.isExist(article))
                        pages.insert(article, pageitr);

                }


                bool operator==( const Data & rhs ){
                    if( this->keyword == rhs.keyword)
                    {
                        return true;
                    }
                    return false;
                }

                bool  operator!=( const Data & rhs ){
                    if( this->keyword != rhs.keyword )
                    {
                        return true;
                    }
                    return false;
                }

            private:
                string keyword;
                List<int> pages;

            };



            int main( )
            {
                List<Data> trialdata;
                ListItr<Data> dataItr;
                ifstream input;  
                string s,k;
                int count = 0;

                string filename = "docdb.txt"; 
                input.open(filename.c_str());

                if (input.fail() )
                {   cout << "could not open file " << filename << endl;
                return 0;
                }

                /*while(input >> s){
                input >> k;
                addtoList(s, stoi(k), *trialdata);

                }*/


                return 0;
            }

The related header to this main is called List.h. It was taken from my course slides. I added one more function and tested it without the object I created. It works fine. Therefore, I'm thinking that there is problem in my object that it can't create a linked list inside a linked list.

#ifndef LIST_H
#define LIST_H
using namespace std; 
#include <iostream>

template <class Object>
class List;     // Incomplete declaration.

template <class Object>
class ListItr;     // Incomplete declaration.

class  BadIterator {
public:
    BadIterator() {}
};


template <class Object>
class ListNode
{
    ListNode( const Object & theElement = Object( ),           
        ListNode * n = NULL )
        : element( theElement ), next( n ) { }

    Object   element;
    ListNode *next;

    friend class List<Object>;
    friend class ListItr<Object>;
};

template <class Object>
class ListItr
{
public:
    ListItr( ) : current( NULL ) { }
    bool isPastEnd( ) const
    { return current == NULL; }
    void advance( )
    { if( !isPastEnd( ) ) current = current->next; }
    const Object & retrieve( ) const
    { if( isPastEnd( ) ) throw BadIterator();
    return current->element; }

private:
    ListNode<Object> *current;    // Current position

    ListItr( ListNode<Object> *theNode )
        : current( theNode ) { }

    friend class List<Object>; // Grant access to constructor
};

template <class Object>
class List
{
private:
    ListNode<Object> *header;

public:
    List( ){
        header = new ListNode<Object>;
    }
    List( const List & rhs );
    ~List( )        {
        makeEmpty( );  // Get rid of all list nodes
        delete header;  // then get rid of the header
    }

    bool isEmpty( ) const{  // see if the header  point to NULL 
        return header->next == NULL;
    }
    void makeEmpty( ){
        while( !isEmpty( ) )
            remove( first( ).retrieve( ) );
    }
    ListItr<Object> zeroth( ) const{
        return ListItr<Object>( header );
    }
    ListItr<Object> first( ) const{
        return ListItr<Object>( header->next );
    }
    void insert( const Object & x, const ListItr<Object> & p ){
        if( p.current != NULL )
            p.current->next = new ListNode<Object>( x, p.current->next );
    }
    ListItr<Object> find( const Object & x ) const{
        ListNode<Object> *itr = header->next; // Initialize

        while( itr != NULL && itr->element != x )
            itr = itr->next;

        return ListItr<Object>( itr );
    }

        bool isExist( const Object & x ) const{
        ListNode<Object> *itr = header->next; // Initialize

        while( itr != NULL){
            if(itr->element == x)
                return true;
            itr = itr->next;
        }
        return false;
    }


    ListItr<Object> findPrevious( const Object & x ) const{
        ListNode<Object> *itr = header;

        while((itr->next != NULL) && itr->next->element != x )
            itr = itr->next;

        return ListItr<Object>( itr );
    }
    void remove( const Object & x ){
        ListItr<Object> p = findPrevious( x );

        if( p.current->next != NULL )
        {
            ListNode<Object> *oldNode = p.current->next;
            p.current->next = p.current->next->next;  // Bypass deleted node
            delete oldNode;
        }
    }

    const List & operator=( const List & rhs ){
        if( this != &rhs )
        {
            makeEmpty( );

            ListItr<Object> ritr = rhs.first( );
            ListItr<Object> itr = zeroth( );
            for( ; !ritr.isPastEnd( ); ritr.advance( ), itr.advance( ) )
                insert( ritr.retrieve( ), itr );
        }
        return *this;
    }

};

#endif
bcperth
  • 2,191
  • 1
  • 10
  • 16
dademolu
  • 31
  • 2

2 Answers2

1

Your header declares a copy-constructor for the List template:

template<class Object>
class List
{
    // ...
    List( const List & rhs );

However, this copy constructor is not defined anywhere.

Therefore, your compiler, when compiling this translation unit, will assume that the template instance will be defined in another translation unit and emits external symbol reference. And since the copy constructor is not defined anywhere, your program fails to link.

The error your compiler reports is a classical undefined symbol error, due to a missing template method instantiation.

You need to implement and define the copy constructor for this template, in your header file.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

You declared a copy constructor for List, but you did not implement it:

template <class Object>
class List
{
    ...
public:
    ...
    List( const List & rhs ); // <-- DECLARATION ONLY!
    ...
};

The linker is complaining that it can't find an implementation for List(const List &) (which is called by the compiler-generated copy constructor for Data when copying the pages member 1).

It is common practice to implement a copy assignment operator using the copy constructor, eg:

template <class Object>
class List
{
    ...    
public:
    ...

    List( const List & src ){
        header = new ListNode<Object>;
        ListItr<Object> sitr = src.first( );
        ListItr<Object> itr = zeroth( );
        for( ; !sitr.isPastEnd( ); sitr.advance( ), itr.advance( ) )
            insert( sitr.retrieve( ), itr );
        }
    }

    ...

    List& operator=( const List & rhs ){
        if( this != &rhs )
        {
            List<Object> temp(rhs);
            std::swap(temp.header, header);
        }
        return *this;
    }
    ...
};

1: BTW, your Data() default constructor is not needed at all. std::string has its own default constructor, so your initialization of the Data::keyword member is redundant, and the constructor is declaring an unused local List variable named pages that shadows the Data:::pages member. Since List also has a default constructor, there is no need to try to initialize the Data::pages member explicitly.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770