Hellp programmers!

I am working on a program that uses two objects of a custom class template List [which is a linked list, with each item pointing to the next] and concatenates them.

Initially, I need to input numbers into my main function, so I have two sets of while loops (while an EOF sequence is not entered) to enter a number into the loop.

Here's my code so far:

//  Program that takes two linked list objects and concatenates
#include <iostream>
#include "List.h"
using namespace std;

int main()
{
    //create two linked lists
    List<int> firstList;
    List<int> secondList;

    //add values to each list
    int input=0;

    cout << "Created firstList and secondList." << endl;

    cout << "\n\nEnter a number to put into firstList (EOF to quit): ";
    while (cin >> input)
    {
        firstList.insertAtBack(input);
        cin.ignore();
        cout << "Enter a number to put into firstList (EOF to quit): ";
    }

    cout << "\n\nThe contents of firstList object of type List are: " << endl;
    firstList.print();

    cout << "\n\nEnter a number to be put into secondList (EOF to quit): ";
    while (cin >> input)
    {
        secondList.insertAtBack(input);
        cin.ignore();
        cout << "Enter a number to be put into secondList (EOF to quit): ";
    }
    cout << "\n\nThe contents of secondList object of type List are: " << endl;
    secondList.print();

    cout << "\n\n\nConcatenating two lists together." << endl;

    //concatenate using a function
}

The first loop works well, but after the statement cout << "\n\nEnter a number to be put into secondList (EOF to quit): ";, the program skips the cin in the loop's decision, goes on without ever running it.

Here's a sample output of what happens:

Created firstList and secondList.


Enter a number to put into firstList (EOF to quit): 1
Enter a number to put into firstList (EOF to quit): 2
Enter a number to put into firstList (EOF to quit): 3
Enter a number to put into firstList (EOF to quit): 4
Enter a number to put into firstList (EOF to quit): 5
Enter a number to put into firstList (EOF to quit): 6
Enter a number to put into firstList (EOF to quit): 

The contents of firstList object of type List are: 
The list is: 1 2 3 4 5 6 



Enter a number to be put into secondList (EOF to quit): 

The contents of secondList object of type List are: 
The list is empty.

I use cin.ignore() to flush the '\n' character from the buffer stream, but it seams that it is not working. Can you help?

Nope. That did not work. This is what I have in code:

//  Program that takes two linked list objects and concatenates
#include <iostream>
#include "List.h"
using namespace std;

int main()
{
    //create two linked lists
    List<int> firstList;
    List<int> secondList;

    //add values to each list
    int input=0;

    cout << "Created firstList and secondList." << endl;

    cout << "\n\nEnter a number to put into firstList (EOF to quit): ";
    while (cin >> input)
    {
        firstList.insertAtBack(input);
        cin.ignore();
        cout << "Enter a number to put into firstList (EOF to quit): ";
    }
    cin.clear();

    cout << "\n\nThe contents of firstList object of type List are: " << endl;
    firstList.print();

    cout << "\n\nEnter a number to be put into secondList (EOF to quit): ";
    while (cin >> input)
    {
        secondList.insertAtBack(input);
        cin.ignore();
        cout << "Enter a number to be put into secondList (EOF to quit): ";
    }
    cout << "\n\nThe contents of secondList object of type List are: " << endl;
    secondList.print();

    cout << "\n\n\nConcatenating two lists together." << endl;

    //concatenate using a function
}

Note the cin.clear() after the first while loop.

Here's my output (and the second loop was skipped again):

Created firstList and secondList.


Enter a number to put into firstList (EOF to quit): 1
Enter a number to put into firstList (EOF to quit): 2
Enter a number to put into firstList (EOF to quit): 4
Enter a number to put into firstList (EOF to quit): 5
Enter a number to put into firstList (EOF to quit): 

The contents of firstList object of type List are: 
The list is: 1 2 4 5 



Enter a number to be put into secondList (EOF to quit): 

The contents of secondList object of type List are: 
The list is empty.




Concatenating two lists together.

Do cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') before the clear just to make sure the buffer is empty. I think there is still a newline in the buffer. You will need to include <limits> for this.

/...
}
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
cin.clear();

cout << "\n\nThe contents of firstList object of type List are: " << endl;
firstList.print();

Nope....

Here's what happens:

//  Program that takes two linked list objects and concatenates
#include <iostream>
#include <limits>
#include "List.h"
using namespace std;

int main()
{
    //create two linked lists
    List<int> firstList;
    List<int> secondList;

    //add values to each list
    int input=0;

    cout << "Created firstList and secondList." << endl;

    cout << "\n\nEnter a number to put into firstList (EOF to quit): ";
    while (cin >> input)
    {
        firstList.insertAtBack(input);
        cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
        cout << "Enter a number to put into firstList (EOF to quit): ";
    }
    cin.clear();

    cout << "\n\nThe contents of firstList object of type List are: " << endl;
    firstList.print();

    cout << "\n\nEnter a number to be put into secondList (EOF to quit): ";
    while (cin >> input)
    {
        secondList.insertAtBack(input);
        cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
        cout << "Enter a number to be put into secondList (EOF to quit): ";
    }
    cout << "\n\nThe contents of secondList object of type List are: " << endl;
    secondList.print();

    cout << "\n\n\nConcatenating two lists together." << endl;

    //concatenate using a function
}

And here's what my output is:

Created firstList and secondList.


Enter a number to put into firstList (EOF to quit): 1
Enter a number to put into firstList (EOF to quit): 2
Enter a number to put into firstList (EOF to quit): 3
Enter a number to put into firstList (EOF to quit): 4
Enter a number to put into firstList (EOF to quit): 5
Enter a number to put into firstList (EOF to quit): 6
Enter a number to put into firstList (EOF to quit): 7
Enter a number to put into firstList (EOF to quit): 

The contents of firstList object of type List are: 
The list is: 1 2 3 4 5 6 7 



Enter a number to be put into secondList (EOF to quit): 

The contents of secondList object of type List are: 
The list is empty.




Concatenating two lists together.

I tried to find some articles explaining the nuances of cin, cin.ignore(), and cin.clear().

Do you know any good resources on this matter?

Edited 2 Years Ago by nathan.pavlovsky: Incorrect spelling

You need the ingnore outside the while loop like I posted. When cin gets the EOF it set the EOF flag which causes cin >> input to be false and the while loop will not execute the last time. I actually switched how it should be done. You need to call clear before you call ignore so it should look like this:

/... end of first while loop
}
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')


cout << "\n\nThe contents of firstList object of type List are: " << endl;
firstList.print();
//...

OK. Here's what I have:

//  Program that takes two linked list objects and concatenates
#include <iostream>
#include <limits>
#include "List.h"
using namespace std;

int main()
{
    //create two linked lists
    List<int> firstList;
    List<int> secondList;

    //add values to each list
    int input=0;

    cout << "Created firstList and secondList." << endl;

    cout << "\n\nEnter a number to put into firstList (EOF to quit): ";
    while (cin >> input)
    {
        firstList.insertAtBack(input);
        cout << "Enter a number to put into firstList (EOF to quit): ";
    }
    cin.clear();
    cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');

    cout << "\n\nThe contents of firstList object of type List are: " << endl;
    firstList.print();

    cout << "\n\nEnter a number to be put into secondList (EOF to quit): ";
    while (cin >> input)
    {
        secondList.insertAtBack(input);
        cout << "Enter a number to be put into secondList (EOF to quit): ";
    }
    cin.clear();
    cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');


    cout << "\n\nThe contents of secondList object of type List are: " << endl;
    secondList.print();

    cout << "\n\n\nConcatenating two lists together." << endl;

    //concatenate using a function
}

When I use the EOF sequence to exit the first loop, the second loop is not run, but if I enter an invalid value, like an alphabetical character, then the first loop ends, and the second one runs. Why does this happen?

Here's an example of this scenario:

Created firstList and secondList.


Enter a number to put into firstList (EOF to quit): 1
Enter a number to put into firstList (EOF to quit): 2
Enter a number to put into firstList (EOF to quit): 3
Enter a number to put into firstList (EOF to quit): 4
Enter a number to put into firstList (EOF to quit): 
s


The contents of firstList object of type List are: 
The list is: 1 2 3 4 



Enter a number to be put into secondList (EOF to quit): 3
Enter a number to be put into secondList (EOF to quit): 4
Enter a number to be put into secondList (EOF to quit): 25
Enter a number to be put into secondList (EOF to quit): 

The contents of secondList object of type List are: 
The list is: 3 4 25 




Concatenating two lists together.

I also want the program to check if the input is a valid number, and not to leave the loop collecting the information if an alphabetical char is entered, but rather to raise an error, and then make the user enter a correct input.

How can I do this?

What is 'List.h' post the code.

I also want the program to check if the input is a valid number,
Simple enough, although you'll have to accept all input as strings, that way you won't get the issues you are having flushing the input stream, if that is indeed the issue.

E.g does string contain only characters 0-9, and check numbers don't have any leading zeros.

Edited 2 Years Ago by iamthwee

List.h is the class template definition of a LinkedList class template.
The theory behind it is explained here: http://www.cprogramming.com/tutorial/lesson15.html

Everything works just fine. Each item in the LinkedList is an class-template object ListNode.

Here's the whole lot of files, if you so desire to understand how it works.

//  List class-template definition

#ifndef List_h
#define List_h

#include <iostream>
#include "ListNode.h"


template <typename NODETYPE>
class List
{
public:

    //default constructor
    List(): firstPtr(nullptr),lastPtr(nullptr)
    {}

    //destructor
    ~List()
    {
        emptyList();
        std::cout << "All nodes destroyed\n\n";
    }

    //insert node at front of List
    void insertAtFront(const NODETYPE& value)
    {
        ListNode<NODETYPE> *newPtr=getNewNode(value);   //new node

        if (isEmpty())                                  //List is empty
            firstPtr=lastPtr=newPtr;                    //new List has only one node
        else
        {
            //List is not empty
            newPtr->nextPtr=firstPtr;
            firstPtr=newPtr;
        }
    }


    //insert node at back of List
    void insertAtBack(const NODETYPE& value)
    {
        ListNode<NODETYPE> *newPtr=getNewNode(value);   //new node

        if (isEmpty())
            firstPtr=lastPtr=newPtr;                    //new List has only one node
        else
        {
            //list is not empty
            lastPtr->nextPtr = newPtr;                  //update previous last node
            lastPtr = newPtr;
        }
    }

    //delete node from front of list [return boolean to signify if operation is successful]
    bool removeFromFront(NODETYPE& value)
    {
        if (isEmpty())      //List is empty
            return false;   //delete unucessful
        else
        {
            ListNode<NODETYPE> *tempPtr=firstPtr;       //hold item to delete

            if (firstPtr==lastPtr)                      //no nodes remain after removal
                firstPtr=lastPtr=nullptr;               //set all ptr's to nullptr
            else                                        //some nodes remain after removal
                firstPtr=firstPtr->nextPtr;             //set firstPtr to nextPtr

            //return data being removed via the reference variable value
            value=tempPtr->data;                        //return the data being removed
            delete tempPtr;                             //reclaim previous front node
            return true;
        }
    }

    //delete node from back of list [return boolean to signify if operation is successful
    bool removeFromBack(NODETYPE& value)
    {
        if (isEmpty())      //List is empty
            return false;   //delete unsuccessful

        else
        {
            ListNode<NODETYPE> *tempPtr=lastPtr;         //hold item to delete

            if (firstPtr == lastPtr)                     //no nodes remain after removal
                firstPtr=lastPtr=nullptr;                //set all ptr's to nullptr
            else
            {
                ListNode<NODETYPE>* currentPtr = firstPtr;

                //locate second-to-last element
                while (currentPtr->nextPtr != lastPtr)
                    currentPtr=currentPtr->nextPtr;      //move to next node

                lastPtr=currentPtr;                      //remove last node
                currentPtr->nextPtr = nullptr;           //this is now the last node
            }

            value = tempPtr->data;                       //return value from old last node
            delete tempPtr;                              //reclaim former last node
            return true;
        }
    }

    //returns true if List is empty
    bool isEmpty() const
    {
        return firstPtr==nullptr;
    }

    //display contents of List
    void print() const
    {
        if (isEmpty())  //List is empty
        {
            std::cout << "The list is empty.\n\n";
            return;
        }

        //continue by printing the list's contents
        ListNode<NODETYPE> *currentPtr=firstPtr;

        std::cout << "The list is: ";
        while (currentPtr != nullptr)       //get element data
        {
            std::cout << currentPtr->data << ' ';
            currentPtr = currentPtr->nextPtr;
        }
        std::cout << "\n" << std::endl;
    }

    List<NODETYPE> operator=(const List<NODETYPE>& toAssign)
    {
        //check to see if the list is the same, to avoid assigning to self
        if (this != &toAssign)
        {
            //empty the list being assigned to, and then assign toAssign's contents to this List
            emptyList();

            //iterate over toAssign's contents, and assign the contents to list
            List<NODETYPE> *currentPtr=toAssign.firstPtr;
            while (currentPtr != nullptr)
            {
                this->insertAtFront(currentPtr);
                currentPtr=currentPtr->nextPtr;     //get toAssign's next content
            }
        }

        return *this;
    }

    NODETYPE& operator[](int subscript)
    {
        if ( subscript < 0 || subscript >= size())
            throw std::out_of_range("Subscript out of range");

        //subscript is in valid range -
        //return a modifiable lvalue
        int count=0;
        ListNode<NODETYPE> *currentPtr=firstPtr;

        while (count != subscript)
        {
            ++count;
            currentPtr=currentPtr->nextPtr;
        }

        return (currentPtr->data);
    }

    NODETYPE operator[](int subscript) const//returns non-modifiable rvalue
    {
        if (subscript < 0 || subscript >= size())
            throw std::out_of_range("Subscript out of range");

        //subscript is in valid range -
        //return a non-modifiable lvalue
        int count=0;
        ListNode<NODETYPE> *currentPtr=firstPtr;

        while (count != subscript)
        {
            ++count;
            currentPtr=currentPtr->nextPtr;
        }

        return (currentPtr->data);
    }
    unsigned size()                         //returns list size
    {
        if (isEmpty())                      //List is empty
            return 0;                       //size is 0
        else
        {
            ListNode<NODETYPE> *currentPtr=firstPtr;
            int sizeCount=0;
            while (currentPtr != nullptr)
            {
                ++sizeCount;
                currentPtr=currentPtr->nextPtr;
            }

            return sizeCount;
        }
    }

    void emptyList()
    {
        if (!isEmpty()) //List is not empty
        {
            std::cout << "Destroying nodes ...\n";

            ListNode<NODETYPE> *currentPtr=firstPtr;
            ListNode<NODETYPE> *tempPtr=nullptr;

            while (currentPtr != nullptr) //delete remaining nodes
            {
                tempPtr=currentPtr;
                std::cout << tempPtr->data << '\n';
                currentPtr = currentPtr->nextPtr;
                delete tempPtr;
            }
        }
    }


private:
    ListNode<NODETYPE> *firstPtr;           //pointer to first node
    ListNode<NODETYPE> *lastPtr;            //pointer to last node

    //utility function to allocate new node
    ListNode<NODETYPE> *getNewNode(const NODETYPE& value)
    {
        return new ListNode<NODETYPE>(value);
    }

};
#endif

And ListNode.h:

//  ListNode class-template definition

#ifndef ListNode_h
#define ListNode_h

//forward declaration of class List is required to announce that class
//List exists so it can be used in the friend declaration at line 20
template<typename NODETYPE> class List;

template<typename NODETYPE>
class ListNode
{
    friend class List<NODETYPE>;    //make List a friend

public:

    //constructor
    explicit ListNode(const NODETYPE &info): data(info), nextPtr(nullptr)
    {}

    //return all data in node
    NODETYPE getData() const
    {
        return data;
    }

private:
    NODETYPE data;                  //data
    ListNode<NODETYPE> *nextPtr;    //pointer to next node in list
};


#endif

Ok ... you have multiple potential problems.

It is often best to solve them one at a time.

A 1st problem is your driver test program.

So here, I just use the STL list to show a ...
'proof of concept' ... working test program.

Once you have all the bugs out there ...

then with a well tested list ...
(i.e. after all aspects of that list are tested, except for the 'glue' part)

then attack the problem of your 'glue' function that joins two lists of your own home grown design.

Here is an example of a 'proof of concept' test program that uses the well tested STL list:

// glueTwoLists.cpp //

//  Program that takes two linked list objects and
// 'glues' second' list to end of first ...

#include <iostream>
#include <string>
#include <list>

using namespace std;

// some utilities used here ...

string takeInString( const string& msg = "" )
{
    cout << msg << flush;
    string val;
    getline( cin, val );
    return val;
}
char takeInChr( const std::string& msg = "" )
{
    string reply = takeInString( msg );
    if( reply.size() )
        return reply[0];
    // else ...
    return 0;
}
bool more()
{
    if( tolower( takeInChr( "More (y/n) ? " )) == 'n' )
        return false;
    // else ...
    return true;
}
int takeInValidInt( const string& msg )
{
    int val;
    while( true )
    {
        cout << msg << flush;
        if( cin >> val && cin.get() == '\n' )
            break;
        else
        {
            cout << "\nOnly integers valid here ...\n";
            cin.clear(); // clear error flags
            cin.sync(); // 'flush' cin stream ...
        }
    }
    return val;
}
// def'n of overloaded << for a list of object type T ...
template< typename T >
ostream& operator << ( ostream& os, const list< T > lst )
{
    typename list< T >::const_iterator cit;
    for( cit = lst.begin(); cit != lst.end(); ++cit )
    {
        os << *cit << endl;
    }
    return os;
}


// a poor emulation of the result desired ...
// in some 'home-grown' double-linked list
template< typename T >
list < T >& glueAtEnd( list< T >& a, list< T >& b )
{
    while( !b.empty() )
    {
        a.push_back( b.front() );
        b.pop_front();
    }
    return a;
}



int main()
{
    cout << "Get some data into firstList and secondList ...\n";
    list <int> firstList;
    do
    {
        int tmp = takeInValidInt( "Enter a number to add "
                                  "to firstList: " );
        firstList.push_back( tmp );
    }
    while( more() );

    cout << "\nHere is the firstList: \n"
         << firstList
         << "with size: " << firstList.size() << endl;

    cout << endl;
    list <int> secondList;
    do
    {
        int tmp = takeInValidInt( "Enter a number to add "
                                  "to secondList: " );
        secondList.push_back( tmp );
    }
    while( more() );
    cout << "\nHere is the secondList: \n"
         << secondList
         << "with size: " << secondList.size() << endl;


    cout << "\nNow here is 'glued' list:\n"
         << glueAtEnd( firstList, secondList );

    cout << "with firstList.size() now = " << firstList.size()
         << "\nwith secondList.size() now = " << secondList.size()
         << endl;
}

This is what I have done now.... and it works!

//  Program that takes two linked list objects and concatenates
#include <iostream>
#include <string>
#include "List.h"
using namespace std;

//////////////////////////////////////////////////////////

//utility functions used here

string takeInString(const string& msg="")
{
    cout << msg << flush;
    string val;
    getline(cin,val);
    return val;
}

char takeInChr(const string& msg="")
{
    string reply=takeInString(msg);
    if (reply.size())
        return reply[0];

    //else...
    return 0;
}

bool more()
{
    char result=tolower(takeInChr("More (y/n) ? "));
    if (result == 'n')
        return false;
    else if (result == 'y')
        return true;
    else
    {
        cerr << "You have entered an invalid input." << endl;
        cout << "Please try again." << endl;
        return more();
    }
}

int takeInValidInt(const string& msg)
{
    int val;
    while (true)
    {
        cout << msg << flush;

        if (cin >> val && cin.get() == '\n')    //valid input
            break;
        else
        {
            cerr << "\nOnly integers are valid here ...\n";
            cin.clear();
            cin.sync();
        }
    }
    return val;
}

template<typename T>
void glueAtEnd(List<T>& a,List<T>& b)
{
    T value;
    while (!b.isEmpty())
    {
        a.insertAtBack(b[0]);
        b.removeFromFront(value);
    }
}

//////////////////////////////////////////////////////////

int main()
{
    cout << "Get some data into firstList and secondList ...\n";

    //create two linked lists
    List<int> firstList;
    List<int> secondList;

    do
    {
        int temp=takeInValidInt("Enter a number to add to firstList: ");
        firstList.insertAtBack(temp);
    } while (more());

    cout << "\nHere is the firstList with size: " << firstList.size() << endl;
    firstList.print();

    do {
        int temp=takeInValidInt("Enter a number to add to secondList: ");
        secondList.insertAtBack(temp);
    } while (more());

    cout << "\nHere is the secondList with size: " << secondList.size() << endl;
    secondList.print();

    cout << "\nNow here is the 'glued' list:\n";
    glueAtEnd(firstList, secondList);
    firstList.print();

}

However, why did my previous example did not work, compared to this?

This is not the method that you really want to use ... is it?

    template<typename T>
    void glueAtEnd( List<T>& a, List<T>& b )
    {
        T value;
        while( !b.isEmpty() )
        {
            a.insertAtBack(b[0]);
            b.removeFromFront(value);
        }
    }

(Recall that I said that my example using STL list was a 'poor' emulation of what you really wanted to do.)

I presume that you want to update the 'tail Node next pointer' ... to point to the head Node of the list you wanted appended ... and to have the 2nd list head Node prev pointer to be updated to point to the tail Node in the 1st list ... yes/no?

If you are coding your own List class, you can code a member function (or a friend function) to do the above.

When the 2nd list has been 'glued' to the end of the first list, then you will want to 'fix' the head and tail and size private members of the 2nd list ... to then reflect that the 2nd list is now 'empty'.

When using cin for student type numeric input, I like to always validate the input and keep the cin error flags 'cleared' and the cin stream 'flushed' ... to prevent 'skipping' the next input because there was an error state or some char's like the '\n' char were left behind by the previous cin input call.

Edited 2 Years Ago by David W

This question has already been answered. Start a new discussion instead.