Link to home
Start Free TrialLog in
Avatar of Steve34
Steve34Flag for United States of America

asked on

Small Program, Small mystery.


I have a small console program ( non-windows, using the msvc++ 5.0 compiler ) below.

The program reads in strings from an input file, puts the string in an object from class node (homemade) and then connects the object to a linked list ( class list, also homemade).

Instead of getting the strings outputted to the screen all I get are is the number "1" for each string.  

Using a ton of test statements (cout) I've learned that the string data is making it into the indvidual nodes of the linked list (objects of class node) and that the nodes are buing built into a proper list.  All the output and list scrolling functions also work.

I think the problem is in class node.  When a node is created it takes in the string data through the constructor. A special function is used to return this value.  It just returns the string via a variable.

Using test statements I can output the strings from the constructor, but not from the reporting function or elsewhere in the class.

It has been a while since I have done OO, console programming, or linked lists.  I'm sure I'm missing something obvious, but I am in that head space where I have looked at it a thousand times and am preventing myself from catching the bug.

The code is below.

Anyone care to take a shot at it?



/**************************************************************************************
**
**      File      :      node.h
**
**      Purpose      :      This class is an element in a linked list.  It is the
**                  empty box that data is dumped in before those boxes are linked
**                together into a "linked list " by another class or external f(x)s
**
**      Created      :      Steve Russell 10/98
*************************************************************************************
*/

#ifndef NODE_H_
#define NODE_H_

#include <fstream.h>
#include <iomanip.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

class node
{
  public:
   // PUBLIC MEMBER FUNCTIONS
      
   
      // CLASS CONSTRUCTOR
      node ( char*, char* );  

      // DISPLAY THE STRING pszQuestion
      char* reportQuestion();


      // DISPLAY THE STRING pszQuestion
      char* reportAnswer();

   
      // REPORT THE ADDRESS OF THE NEXT NODE IN THE LIST
      node* report_neibhor();

      // RESET *THIS* NODES PTR TO A NEW DESTINATION
      void change_neibhor( node* new_neibhor );



   private:
      
      // PRIVATE MEMBERS OF THE CLASS
      
      char* pszQuestion;
      char* pszAnswer;
    node* neibhor;  // a ptr pointing to the next link in the list.


};// end of class node.h header file
#endif

/*
************************************************************************************
**                             END OF FILE  node.h
************************************************************************************
*/







/*
****************************************************************************
**
**      File      :      node.C
**
**      Purpose      :      Code for a node in a linked list.
**
**      Created      :      Steve Russell  10/98
**
*****************************************************************************
*/

#include <fstream.h>
#include <iomanip.h>
#include <iostream.h>
#include <string.h>
#include "node.h"

#include <stdlib.h>
#include <assert.h>
#include <ctype.h>

/*
*******************************************************************************
**
**      Function      :      Constructor
**
**      Created            :      Steve Russell 10/98
**
*******************************************************************************
*/
node::node( char* pQuestionBuilder, char* buffer)
{
 
      
      pszQuestion =  pQuestionBuilder;
      pszAnswer      =  buffer;
      
      

}//end node()
/*
*******************************************************************************
**
**      Function      :      reportQuestion
**
**      Purpose            :   Return the string pszQuestion
**
**      Created            :      Steve Russell 10/98
**
*******************************************************************************
*/
char*  node::reportQuestion()
{

      
    return pszAnswer;
      
}

/*
*******************************************************************************
**
**      Function      :      reportAnswer
**
**      Purpose            :   Return the string pszAnswer
**
**      Created            :      Steve Russell 10/98
**
*******************************************************************************
*/
char*  node::reportAnswer()
{
      return pszAnswer;
}
/*
*******************************************************************************
**
**      Function      :      report_neibhor()
**
**      Purpose            :   SHOW THE ADDRESS OF THE NEXT NODE IN THE LIST
**
**      Created            :      Steve Russell 10/98
**
*******************************************************************************
*/

node* node::report_neibhor()
{
  return neibhor;
}
/*
*******************************************************************************
**
**      Function      :      change_neibhor()
**
**      Purpose            :   LINK THIS NODE TO A NEW DESTINATION BY CHANGING THE NEIBHOR POINTER
**
**      Created            :      Steve Russell 10/98
**
*******************************************************************************
*/

void node::change_neibhor( node* new_neibhor )
{
 
      neibhor = new_neibhor;

}

/*
***********************************************************************************
**                                          END OF FILE node.C
***********************************************************************************
*/

/*************************************************************************************
**
**      File      :      list.h
**
**      Purpose      :      The class "list" provides all of the functions for processing a linked
**              list of self contained nodes ( class node ).  It adds new nodes to
**              the end of a list.
**
**      Created      :      Steve Russell 10/98
**
************************************************************************************
*/


#ifndef LIST_H_
#define LIST_H_

#include <fstream.h>
#include <iomanip.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "node.h"

class  list
{
 public:
 
   
  // class constructor
      list ();

  // display a particular item in the list
    void scrollList( int nth );

  // add a node to the bottom of the list
    virtual void add_node( node* );

     
 
  // PROTECTED MEMBERS OF THE CLASS
  protected:
   
  node* head;     // a pointer to the first element in the linked list
  node* tail;     // a pointer to the last  element in the linked list
  int numnodes;   // number of nodes in the linked list

};//end of class list.h header file
#endif


/*
*************************************************************************************
**                               End Of File list.h*************************************************************************************
*/

/**************************************************************************************
**
**      File      :      list.cpp
**
**      Purpose      :   Implements class list, which buildes a linked list of class node nodes
**
**      Created      :      Steve Russell  10/98
**
**
*************************************************************************************
*/

#include <fstream.h>
#include <iomanip.h>
#include <iostream.h>
#include <string.h>
#include "list.h"
#include "node.h"
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>


/*
************************************************************************************
**
**      Function      : list()
**
**      Purpose            : class list constructor
**
**      Created            : Steve Russell 10/98
**
************************************************************************************
*/

list :: list()
{

  head     = 0;
  tail     = 0;
  numnodes = 0;

}

/*
************************************************************************************
**
**      Function      :
**
**      Purpose            :      DISPLAY A VALUE AT A PARTICULAR POSITION IN A LIST
**
**      Created            :      Steve Russell  10/98
**
************************************************************************************
*/
void list::scrollList(int nth)
{

  node* positionptr = head;  // place holder in the list
  int b;                               // counting index for a for-loop



  // CHECK TO SEE IF THE REQUESTED POSITION EXISTS IN THE LIST
  if(  (nth > numnodes) || (nth < 1)  ) //isn't in the range of the list
    cout<<" This List Has No Such Postion.  Sorry."<<endl;


  // SCROLL UP TO DESIRED POSITION AND DISPLAY ITS VALUE
  else  
    {
            for( b=1; b <= numnodes;/*nth;*/ b++ )
            {
                  //if( b == nth)
                  //{
                        cout <<"The Next Question Is: "<<endl;
                        cout << positionptr->reportQuestion()<<endl;
                        
                        //cout <<"That Was The Next Question"<<endl;
                        //cout << endl;
                  //}//end if

                  //else      // MOVE PLACE MARKERS UP
                        positionptr = positionptr->report_neibhor();

            }//end for-loop
    }//end else
}

/*
************************************************************************************
**
**      Function      :      add_node()
**
**      Purpose            :      ADD A NODE AT THE END OF THE LIST
**
**      Created            :      Steve Russell  10/98
**
************************************************************************************
*/
void list::add_node(node* newptr)
{

  // RECLOAK NEWPTR SO IT CAN BE PASSED INTO class node MEMBER FUNCTION
  node* new_neibhor;    
  new_neibhor = newptr;


  // ADD "NEWPTR'S" NODE TO AN EMPTY LIST
  if( numnodes == 0 ) // list is empty
    {
      head = newptr;
      tail = newptr;
      numnodes++;
    }//end if


  // ADD "NEWPTR'S" NODE TO AN EXISTING LIST
  else // add to the end of a non-empty list
    {
      tail->change_neibhor(new_neibhor);
      tail = newptr;
      numnodes++;
        
    }// end else

}// end add_node()
/*
*************************************************************************************
*/


/*
*************************************************************************************
**                                             END OF FILE  list.cpp
*************************************************************************************
*/

/**************************************************************************
                        Project 3

                    CMIS 345 Wed 7-10

                      Steve Russell
                  srussell@nova.umuc.edu



                         Purpose:
                         ********

To practice building and using a variety of object oriented linked
links starting from a general base class and using inheritance to
create specailized O.O. linked lists from the parent class.



                        Files:
                        *****


p.C            ( main program )
list.h, list.C ( parent class )
input_p3.dat    ( input file )
order_q.h, order_q.C ( child  )
p3              (executable )      
queue.h, queue.C     ( child )
stack.h, stack.C     ( child )
node.h, node.C ( a list node  )




                        Usage:
                        ******

The main program ( p.C ) declares an object of each class/subclass
causing the classes to build 4 differnt types of linked lists from the same
input file.  The 4 lists are built in differnt ways depending on the
type of linked list that the class/subclass is.  The main program then
gives the user various options for displaying these linked lists and
removing members from them.




                        Inputs:
                        ******

1. Integers from file input_p3.dat.
2. The User's choices from the menus.
3. Position numbers from the User, as to which list elements to look at.




                         Outputs:
                         *******
                All outputs are to the screen.

1. A chart of the members of each kind of linked list.
2. A main menu with various display options and a remove option.
3. A sub menu for specifying which list to operate on.
4. Various error messages for inproper User inputs.




                        Comments:
                        *********

class node   : is a data holder for elements to be built into a linked list.
class list   : is the parent class for all lists, it includes functions
             for manipulating class node.  It adds nodes to the bottom
             and removes them from the top.
class queue  : Is a child of class list.  It does everything in the same
             way and has the same functions as class list.
class order_q: Is a child of class queue, and a grandchild of class list.
             It sorts nodes by numerical order as it add them to a
             linked list.
class stack  : Is a child of class list. Nodes are added at the top
             and removed from the top.
      
**************************************************************************/
// FILE: project_3.C
// TESTS VARIOUS MEMBER FUNCTIONS OF CLASS "LIST" AND ITS FAMILY

#include <fstream.h>
#include <iomanip.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "list.h"

#include "node.h"

int main ()
{

/*---------------------------------------------------------------------------
                         LOCAL DATA
-----------------------------------------------------------------------------*/

char choice;            // control variable for a switch structure

char buffer[100];
char* pQuestionBuilder =  "Test From P.cpp";



list questionList;


/*---------------------------------------------------------------------------
                        FUNCTION PROTOTYPES
-----------------------------------------------------------------------------*/


// PROCESS THE USER'S REQUEST TO SEE WHAT INT VALUES ARE STORED WHERE

void display_nth_element(list questionList);

/*-----------------------------------------------------------------------------
                          MAIN PROGRAM
-----------------------------------------------------------------------------*/

// OPEN THE INPUT FILE
ifstream ins("health3.txt", ios::in);
      if ( ins.fail() )
      {
      cerr << "***ERROR:CANNOT OPEN INPUT FILE health3.txt FOR INPUT"<<endl;
        exit(-1);
      }

      
// READ IN DATA, SEND IT TO THE OBJECT, AND BUILD THE LISTS
while( !ins.eof())
  {         

      
    ins.getline( buffer, 100);
    // EAT WHITE SPACE SO THE EOF F(X) WORKS CORRECTLY
    ins>>ws;
      
      node* newptr = new node( pQuestionBuilder, buffer);
    questionList.add_node(newptr);
      
      /*
    if ( strlen( buffer ) < 2 )
      {
      
            node* newptr = new node( pQuestionBuilder, buffer);
        questionList.add_node(newptr);
            pQuestionBuilder = NULL;
      }
    else
            strcat( pQuestionBuilder, buffer );
     */
          
  }// endwhile


// DISPLAY THE MAIN MENU
while ( 1 )
  {
    cout<<"***************************************************"<<endl;  
    cout<<"                  * MAIN MENU*                      "<<endl;
    cout<<"***************************************************"<<endl;
    cout<<" Display a SPECIFIC node on a list           ( s )"<<endl;
    cout<<" Exit The Program                            ( e )"<<endl;
    cout<<"***************************************************"<<endl;
    cout<<" You Chose: ";
    // READ IN THE USER'S CHOICE
    cin>> choice;

    // DISPATCH THE USER'S CHOICE TO THE RIGHT F(x)
    switch( choice )
      {
            

            case 's':
            // DISPLAY A PARTICULAR LIST ELEMENT
            display_nth_element(questionList);
            break;
            
            
            case 'e':
            // EXIT THE PROGRAM
                  cout<<endl;
                  cout<<"Have A Nice Day ! :) "<<endl;
                exit (-1);
                  break;
            default:
                  cout<<"Please Pick A Choice From The Menu"<<endl;
                  break;
      }// end switch
  }// end while

return 0;
}// end main
/*---------------------------------------------------------------------------
                  END OF MAIN PROGRAM
-----------------------------------------------------------------------------
                  FUNCTION DEFINITIONS
*/

//***************************************************************************

void display_nth_element(list questionList)
{
      int nth;        // the spot in line to look up.
   


        cout<<"***************************************************"<<endl;  
          cout<<" DISPLAY POSTION NUMBER#?:  ";  
          cin>> nth;
        

      
            questionList.scrollList(nth);
          


}// end display_nth_element
/*****************************************************************************
             END OF FUNCTION DEFINITIONS, END OF MAIN PROGRAM
******************************************************************************/









/********************* input file health3.txt ********************/



A committee sponsored by this organization sets the Recommended Dietary Allowances (RDA):
(1) USDA (2) NIH (3) National Academy of Sciences (4) ADA
3
Saturated fat is found in:
(1) only animal products (2) only vegetable products (3) both animal & vegetable products (4) none of the above
3
Cholesterol is found in:
(1) only animal products (2) only vegetable products
(3) animal & vegetable products (4) none of the above
1
Which is NOT a good source of iron?
(1) blackstrap molasses (2) raisins (3) lentils (4) yogurt
4
Which is NOT a good source of calcium?
(1) broccoli (2) cauliflower (3) kale (4) okra
2
Where can vegans get vitamin D?
(1) nutritional yeast (2) soybeans (3) sunlight (4) cod-liver oil
3
Which is NOT a good source of vitamin C?
(1) potatoes (2) carrots (3) strawberries (4) cantaloupe
2






















ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nietod
nietod

For a simple example, I have simplified your node class down to one data member and a simple constructor.

class node
   {
     public:
   // CLASS CONSTRUCTOR
   node ( char* );  
      private:
   char* pszQuestion;
};// end of class node.h header file

node::node( char* pQuestionBuilder)
   {
   pszQuestion =  pQuestionBuilder;
   }//end node()

now look at the fllowing code for creating nodes (again simplified)

char buffer[100];

ins.getline( buffer, 100);
 node* newptr1 = new node(, buffer);
ins.getline( buffer, 100);      // (2)
 node* newptr1 = new node(, buffer);

Notice that the program reads data into buffer, then builds a node with that data.  That node just keeps a pointer to buffer.    Thus on the line marked (2), when the next line is read in, the buffer is changed.  This in effect changes the data associated with first node.  In this way, all nodes will always have the same string.  That is bad.  Worse, the string buffer is a local variable.  When the function ends it will be destroyed.  But the nodes don't "know" that.  They will continue to use it even though it is destroyed.  Very bad.

What to do follows.


The node class need to keep a "private" copy of the string.  That way each node can have its own string that is not affected by other nodes or other operations in the program.  One way to do this is for the node constructor to alocate memory to store a private copy of  the string.  and then copy in the strign that was passed in the constructor parameters, like

node::node( char* pQuestionBuilder)
{
   int StrLen = strlen(pQuestionBuilder);  // Get the length of the string specified.
   
  pszQuestion =  new char[StrLen + 1];  // Create space for private copy.  Leave room for extra NUL at end.

  strcpy(pszQuestion,pQuestionBuilder); // Copy in the string that was passed.
}//end node()

One important thing however, the space allocated for this string must eventually be freed.  Thus the node's destructor will have to release this space.  Like

node::~node()
{
   delete [] pszQuestion;
}

In addition, if there are times that the string can be changed (especially if its length changes), you will need to take similar actions at those times.

Another option, one I recomend highly to experienced programmers (but not to those starting out, because it is good to do this stuff at the start to learn it), is to use a string class.  The STL has a string class called "string" and defined in the <string> header file.  It will take care of all the allocation and dealocation issues for you as well as many other issues.  But I would recomend you do it the hardway ar first, then change to a string class later.

Let me know if you have questions.
Avatar of Steve34

ASKER

It works.

Thanks for explaining why it DIDN'T work.  That will help me in avoiding trouble in the future.  I'm going to copy my question and your answer into a directory I keep of "solved problems".

If I understand you, the pointers in class node's constructor ( pszQuestion ) start off not pointing to anything.  When class node gets fed a string pszQuestion points to char* in the main program, ......that changes........thus resulting in bogus values.

If I understand your solution, an empty string ( char[] ) is allocated for pszQuestion to point to (instead of the ptr in the main program) and then the contents of the char* from the main program are copied into this empty char[] so that class node can keep it.

I promise I will be a good boy and use the hard method for a while.  I'm sure this issue will come up again and I want to drill it into my head as to what to do.

I am curious.  How would I use <string.h> to do this?  Whould I still use char*, or would I just use char[]  ( or some other data type)?
>> I'm going to copy my question and your answer into a directory
>> I keep of "solved problems"

An excellend idea.  You will make the same mistakes again and again.  The most frustrating thing is knowing that you've seen the same behavor before, but can't remember whatcaused it and how if got fixed...   I keep a diary of my work for this purpose.

Note however, that EE will also keep a copy of this question.  You can always refer back to it here.

>> if I understand you, the pointers in class node's constructor
>> ( pszQuestion ) start off not pointing to anything.  When class
>> node gets fed a string pszQuestion points to char* in the
>> main program,
that is.  They start of with "random" values  That is they point somewhere in memory and using them with those values is certain death.  But that doesn't mater for your purposes, because you initialize them right away (before you use them).  the problem is you initialize them to use a buffer that will be later changed and eventually destroyed.  This sort of use of "char *" pointers is okay in some cases.  It is okay in segment fo code where you know the buffer isn't being used by "anything else" and you know that the buffer will remain throughout the process, like

char buffer[100];

cin.getline(buffer);

char *CurChrPtr = buffer;  // This is okay.

while(*CurChrPtr != ' ')
  ++ ChrChrPtr;
cout << Found a " << *CurChrPtr;

That is okay because we are not saving the pointer to the buffer and then reusing buffer again.  We are just ising the pointer while there is a particular string inside buffer.  but code like.

char buffer[100];

cin.getline(buffer);

char *CurChrPtr = buffer;  // This is okay.

while(*CurChrPtr != ' ')
  ++ ChrChrPtr;

return CurChrPtr;

This "saves" the pointer.  It returns the pointer so that the caller can use it.  That won;t work because when it returns buffer is destroyed and the pointer should be considered invalid.

In summary, you can't save pointers to string buffers unless you know (1) that they will remain in existence as long as you are using the pointer and (2) no other code reuses the buffer.

>> if I understand your solution, an empty string ( char[] ) is allocated ...
Yes.  Except I would not say an empty string.  when it is allocated it will contain "random" garbage values.  You must intiailize it before using it.  Strcpt() does this in this example.

>>How would I use <string.h> to do this?  Whould I still use char*, or would I just
>> use char[]  ( or some other data type)?

You would replace ALMOST all occurances of "char *" with "string"  for example the node would contian two string data members, like

node
{
   string Question;
   string Answer;
}

rather than char * pointers to strings.  The only time you would use char *, would be when you need to process a string, like in the above code you mist still want to have a CurChrPtr char * to search through a string to find a non-space character.  That type of thing.  Note however, it would probably be searching throught a "string" class string not a "char[]" string.  That is, "buffer" would be a "string" class object, not "char []"
Avatar of Steve34

ASKER

Excellent & QUICK answer.

You explained to me why what I did did not work and gave me two possible solutions.

This will help beyond this particular problem in the future.

I really appreciate the help.  Thanks