juicy problem, can someone help? template classes, pointers

J

Joshua Moore

The following code is my attempt to create a program that takes a list
such as this one:
apples taste good
apples are good for you and not expensive at all
you should eat more apples

candy tastes good, too
candy is bad for you and really expensive
you shouldn't eat that much candy
and because i can, a fourth line

and this entry will consist of just one line

<eof>
and write the entries in reverse order, but retaining the order of the
lines within the entry, so the list would look like this:
and this entry will consist of just one line

candy tastes good, too
candy is bad for you and really expensive
you shouldn't eat that much candy
and because i can, a fourth line

apples taste good
apples are good for you and not expensive at all
you should eat more apples

<eof>
What I found so far, is that the variable "start" in object "list" of
class LinkedList is NULL for some reason, but I don't know why.
Can anyone help?

Here comes the code:

//main.cpp
//Joshua Moore
//reverse the order

#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;

//The template class for a linked list
//T should have a == operator defined
template <class T>
class LinkedList{
//private members of LinkedList
private:
//the node class
class node{
private:
T data;
node* next;

public:
node(T input){
data = input;
next = NULL;
}

~node(){}

void setData(T d){
data = d;
}

T getData(){
return data;
}

T* getDataP(){
return &data;
}

void setNext(node* n){
next = n;
}

node* getNext(){
return next;
}
};//end of class node

//a pointer to the first node of the list
node* start;

//the number of items in the list
int items;

//the current retrieve data
node* retrieve;

//returns a pointer to the last node
node* lastNode(){
node* temp = start;

//if there are no nodes
if(temp==NULL)
return NULL;

//otherwise move to the next until there is no more next
while(temp->getNext()!=NULL)
temp = temp->getNext();

//return the last node
return temp;
}

//public members of LinkedList
public:
//Constructor
LinkedList(){
start = NULL;
items = 0;
}

//Deconstructor
~LinkedList(){
}

//returns the number of items in the list
int getItems(){
return items;
}

//stores the data given
void store(T data){
//if this node will be at the beginning of the list
if(start==NULL){
//create the first node
start = new node(data);
items++;
}else{
//otherwise create a new node at the end of the list
lastNode()->setNext(new node(data));
items++;
}
cout << "item successfully stored at " << lastNode() << endl;
}//end of store

//returns a pointer to the data containing the argument
T* find(T data){
node* temp = start;

//move on to the next node until there is no next node
while(temp->next!=NULL){
//if the node contains the specified data, return its
if(temp->data==data)
return temp->getDataP();

temp = temp->getNext();
}

//if the node was not found, return NULL
return NULL;
}//end of find

//deletes the first node containing data
void deleteNode(T data){
node* target = find(data);

//if data was not found
if(target==NULL){
//do nothing
}else{
//if the target is the first node
if(target==start){
//save the second node
node* temp = start->getNext();

//delete the first node
delete start;
items--;

//make the saved node the first one
start = temp;
}else{
//otherwise find the node before the target
node* before = start;
while(before->getNext()!=target)
before = before->getNext();

//connect the node before with the node after the target
before->setNext(target->getNext());

//delete the target
delete target;
items--;
}
}
}

//resets retrieve to the first node
void retrieveFirst(){
retrieve = start;
}

//resets retrieve to the last node
void retrieveLast(){
cout << "retrieveLast() was called" << endl;

retrieve = start;
cout << "retrieve set to start" << endl;

cout << "start: " << start << endl;

cout << "retrieve: " << retrieve << endl;

while(retrieve->getNext()!=NULL){
retrieve = retrieve->getNext();
}
}

//sets retrieve to the next node,
//returns true if successful, false if not
bool retrieveNext(){
if(retrieve->getNext()!=NULL){
retrieve = retrieve->getNext();

cout << "retrieveNext() returns true" << endl;
return true;
}else{
cout << "retrieveNext() returns false" << endl;
return false;
}
}

//sets retrieve to the previous node,
//returns true if successful, false if not
bool retrievePrev(){
node* temp = retrieve;
if(temp==start){
return false;
}

while(temp->getNext()!=retrieve){
temp = temp->getNext();
}

retrieve = temp;
return true;
}

//returns the data of current retrieve
T retrieveData(){
return retrieve->getData();
}

//returns a pointer to the data of current retrieve
T* retrieveDataP(){
return retrieve->getDataP();
}

//returns the value of start(just for diagnostics)
T* retrieveStart(){
return start;
}
};//end of class LinkedList

//function prototypes
void readfile(LinkedList< LinkedList<string> > list);
void writefile(LinkedList< LinkedList<string> > list);

int main(int argc, char *argv[])
{
LinkedList< LinkedList<string> > list;

readfile(list);

cout << "==========================" << endl;

writefile(list);

system("PAUSE");
return EXIT_SUCCESS;
}

//file may not be empty
//report entry must end with newline character
void readfile( LinkedList<LinkedList<string> > list){
ifstream input;
input.open("reports.txt");
if(input.fail()){
cerr << "error, couldn't open 'reports.txt'" << endl;
}

string temp;

do{
LinkedList<string> wrapper;

getline(input, temp);
//if there are characters in the line
if(temp.length()>0){
//store lines in a wrapper
wrapper.store(temp);
}else{
//otherwise store wrapper in list
list.store(wrapper);
}
}while(!input.eof());

input.close();
}//readfile

void writefile(LinkedList< LinkedList<string> > list){
ofstream output;
output.open("reports output.txt");
if(output.fail()){
cerr << "error, couldn't open 'reports output.txt'" << endl;
}

cout << "file was successfully opened" << endl;
cout << "list.start: " << list.retrieveStart() << endl;

//set list's retrieve to Last
list.retrieveLast();
cout << "list.retrieveLast()" << endl;

//set wrapper's retrieve to First
list.retrieveDataP()->retrieveFirst();

//write first wrapper's data to file
output << list.retrieveDataP()->retrieveData() << endl;
while(list.retrieveDataP()->retrieveNext()){
output << list.retrieveDataP()->retrieveData() << endl;
}

//write an endline after the first wrapper;
output << "\n";

//write subsequent wrappers' data to file
while(list.retrievePrev()){
output << list.retrieveDataP()->retrieveData() << endl;
while(list.retrieveDataP()->retrieveNext()){
output << list.retrieveDataP()->retrieveData() << endl;
}
//write an endline after the wrapper
output << "\n";
}

output.close();
}//writefile
 
B

BobR

Joshua Moore said:
The following code is my attempt to create a program that takes a list
such as this one: [snip]

file://function prototypes
// > void readfile(LinkedList< LinkedList<string> > list);

void readfile(LinkedList said:
void writefile(LinkedList< LinkedList<string> > list);

int main(int argc, char *argv[]){
LinkedList< LinkedList<string> > list;

readfile( list );

You pass the 'list' by value, fill it, then discard the changes.
Pass it by 'non-const reference' ('&', no change to line above), and see
what happens.
cout << "==========================" << endl;

writefile(list);

system("PAUSE");
return EXIT_SUCCESS;
}

file://file may not be empty
file://report entry must end with newline character
// > void readfile( LinkedList<LinkedList<string> > list){

void readfile( LinkedList<LinkedList<string> > &list){

// > ifstream input;
// > input.open("reports.txt");

ifstream input( "reports.txt" );
 
J

Joshua Moore

Joshua Moore said:
The following code is my attempt to create a program that takes a list
such as this one: [snip]

file://function prototypes

// > void readfile(LinkedList< LinkedList<string> > list);

void readfile(LinkedList said:
void writefile(LinkedList< LinkedList<string> > list);
int main(int argc, char *argv[]){
LinkedList< LinkedList<string> > list;
readfile( list );

You pass the 'list' by value, fill it, then discard the changes.
Pass it by 'non-const reference' ('&', no change to line above), and see
what happens.


cout << "==========================" << endl;

system("PAUSE");
return EXIT_SUCCESS;
}
file://file may not be empty
file://report entry must end with newline character

// > void readfile( LinkedList<LinkedList<string> > list){

void readfile( LinkedList<LinkedList<string> > &list){

// > ifstream input;
// > input.open("reports.txt");

ifstream input( "reports.txt" );


if( input.fail() ){
cerr << "error, couldn't open 'reports.txt'" << endl;
}
string temp;
do{
LinkedList<string> wrapper;
getline(input, temp);
file://if there are characters in the line
if(temp.length()>0){
file://store lines in a wrapper
wrapper.store(temp);
}else{
file://otherwise store wrapper in list
list.store(wrapper);
}
}while(!input.eof());
input.close();
}//readfile

Thanks Bob!
Haha, I feel really stupid about the pass by reference - I never would
have caught that at this point. I changed that, and the program runs
until before

//write first wrapper's data to file
output << list.retrieveDataP()->retrieveData() << endl;

in function void writefile(LinkedList<LinkedList<string> > &list).
After that it crashes with an "unhandled win32 exception in main.exe
[4080]." Unfortunately I don't know what that means. Do you know more?
 
J

John Harrison

Thanks Bob!
Haha, I feel really stupid about the pass by reference - I never would
have caught that at this point. I changed that, and the program runs
until before

//write first wrapper's data to file
output << list.retrieveDataP()->retrieveData() << endl;

in function void writefile(LinkedList<LinkedList<string> > &list).
After that it crashes with an "unhandled win32 exception in main.exe
[4080]." Unfortunately I don't know what that means. Do you know more?

Unfortunately it means your code has bugs. Maybe lots of bugs.

The most obvious bug is that your list class lacks a copy constructor
and assignment operator. This means you cannot safely copy a list, yet
your code is copying lists all over the place. From function readfile

list.store(wrapper);

This line obviously copies the list 'wrapper' into the list 'list'.

So you need the following

template <class T>
class LinkedList
{
public:

LinkedList(const LinkedList& rhs)
{
// your code here to copy a list here
}
LinkedList& operator=(const LinkedList& rhs)
{
// your code here to assign a list here
// this mean getting rid of the old list, and
// copying over the new list
}
};

Now either you just forgot that you would need a copy constructor and
assignment operator, or you're doing a very strange course where you're
taught about templates and linked lists before you've been told about
the need for copy constructors and assignment operators.

Have you heard of the 'rule of three'? If your class needs any one of a
destructor, copy constructor or assignment operator it problably needs
all three.

If this is all unfamiliar to you, then you need a good book, or a long
talk to your tutor.

john
 
J

Joshua Moore

So you need the following
template <class T>
class LinkedList
{
public:

LinkedList(const LinkedList& rhs)
{
// your code here to copy a list here
}
LinkedList& operator=(const LinkedList& rhs)
{
// your code here to assign a list here
// this mean getting rid of the old list, and
// copying over the new list
}

};
Thanks a lot! I did not know that.
Now either you just forgot that you would need a copy constructor and
assignment operator, or you're doing a very strange course where you're
taught about templates and linked lists before you've been told about
the need for copy constructors and assignment operators.

Have you heard of the 'rule of three'? If your class needs any one of a
destructor, copy constructor or assignment operator it problably needs
all three.

If this is all unfamiliar to you, then you need a good book, or a long
talk to your tutor.

The class I took didn't get farther than reference parameters, structs
and file i/o. What I know beyond that, I taught my self through the
internet, books and trial and error. This is not an assignment... I
really need to reverse a list and don't want to do it by hand since
this is something that will come up more often. Thanks for your great
tips!

-Joshua.
 
K

Kai-Uwe Bux

Joshua said:
Thanks a lot! I did not know that.


The class I took didn't get farther than reference parameters, structs
and file i/o. What I know beyond that, I taught my self through the
internet, books and trial and error. This is not an assignment... I
really need to reverse a list and don't want to do it by hand since
this is something that will come up more often. Thanks for your great
tips!

Well, if you just need a list that works, I suggest you have a look into the
standard library. The header <list> provides a list data structure. The
header <algorithm> has a lot of goodies that work on sequences. There is no
need to reinvent the wheel.

Using the standard library, you could approach the original problem as
follows (untested code, won't compile, just for illustration):

typedef std::vector< std::string > paragraph;
typedef std::vector< paragraph > text;

bool read_paragraph ( std::istream & istr, paragraph & p );

bool write_paragraph ( std::eek:stream & ostr, paragraph const & p );

int main ( void ) {
text the_list;
paragraph the_par;
while ( read_paragraph( std::cin, the_par ) ) {
the_list.push_back( the_par );
}
for ( text::const_reverse_iterator iter = the_list.rbegin();
iter != the_list.rend(); ++ iter ) {
write_paragraph( std::cout, p );
}
}

Note the use of const_reverse_iterator to do the inversion of the order.


Best

Kai-Uwe Bux
 
J

Joshua Moore

Note the use of const_reverse_iterator to do the inversion of the order.

This really helped a lot. I used a list instead of a vector, and
back()/front() and pop_back()/pop_front() instead of an iterator and
it works great! Now I can go on to the next task of writing the data
into a .csv file.
 
B

BobR

Joshua Moore said:
This really helped a lot. I used a list instead of a vector, and
back()/front() and pop_back()/pop_front() instead of an iterator and
it works great! Now I can go on to the next task of writing the data
into a .csv file.

[ from prior post ]
This is not an assignment...

Wish you had mentioned that to begin with. I could have given you a better
answer (std::list, std::reverse, etc., (like Mr. Bux mentioned). ). <G>

You may be interested in these books if you don't have them yet. In volume
two, Mr. Eckel shows some of the standard C++ library usage, which may help
you (that's in *addition* to your other book(s)).

Get "Thinking in C++", 2nd ed. Volume 1&2 by Bruce Eckel
(available for free here. You can buy it in hardcopy too.):
http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html

Another place to visit:

The Dinkumware site has good documentation:
http://www.dinkumware.com/manuals/.
 
J

Jim Langston

Joshua Moore said:
The following code is my attempt to create a program that takes a list
such as this one:
apples taste good
apples are good for you and not expensive at all
you should eat more apples

candy tastes good, too
candy is bad for you and really expensive
you shouldn't eat that much candy
and because i can, a fourth line

and this entry will consist of just one line

<eof>
and write the entries in reverse order, but retaining the order of the
lines within the entry, so the list would look like this:
and this entry will consist of just one line

candy tastes good, too
candy is bad for you and really expensive
you shouldn't eat that much candy
and because i can, a fourth line

apples taste good
apples are good for you and not expensive at all
you should eat more apples

<eof>
What I found so far, is that the variable "start" in object "list" of
class LinkedList is NULL for some reason, but I don't know why.
Can anyone help?

Here comes the code:

//main.cpp
//Joshua Moore
//reverse the order

#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;

//The template class for a linked list
//T should have a == operator defined
template <class T>
class LinkedList{
//private members of LinkedList
private:
//the node class
class node{
private:
T data;
node* next;

public:
node(T input){
data = input;
next = NULL;
}

~node(){}

void setData(T d){
data = d;
}

T getData(){
return data;
}

T* getDataP(){
return &data;
}

void setNext(node* n){
next = n;
}

node* getNext(){
return next;
}
};//end of class node

//a pointer to the first node of the list
node* start;

//the number of items in the list
int items;

//the current retrieve data
node* retrieve;

//returns a pointer to the last node
node* lastNode(){
node* temp = start;

//if there are no nodes
if(temp==NULL)
return NULL;

//otherwise move to the next until there is no more next
while(temp->getNext()!=NULL)
temp = temp->getNext();

//return the last node
return temp;
}

//public members of LinkedList
public:
//Constructor
LinkedList(){
start = NULL;
items = 0;
}

//Deconstructor
~LinkedList(){
}

//returns the number of items in the list
int getItems(){
return items;
}

//stores the data given
void store(T data){
//if this node will be at the beginning of the list
if(start==NULL){
//create the first node
start = new node(data);
items++;
}else{
//otherwise create a new node at the end of the list
lastNode()->setNext(new node(data));
items++;
}
cout << "item successfully stored at " << lastNode() << endl;
}//end of store

//returns a pointer to the data containing the argument
T* find(T data){
node* temp = start;

//move on to the next node until there is no next node
while(temp->next!=NULL){
//if the node contains the specified data, return its
if(temp->data==data)
return temp->getDataP();

temp = temp->getNext();
}

//if the node was not found, return NULL
return NULL;
}//end of find

//deletes the first node containing data
void deleteNode(T data){
node* target = find(data);

//if data was not found
if(target==NULL){
//do nothing
}else{
//if the target is the first node
if(target==start){
//save the second node
node* temp = start->getNext();

//delete the first node
delete start;
items--;

//make the saved node the first one
start = temp;
}else{
//otherwise find the node before the target
node* before = start;
while(before->getNext()!=target)
before = before->getNext();

//connect the node before with the node after the target
before->setNext(target->getNext());

//delete the target
delete target;
items--;
}
}
}

//resets retrieve to the first node
void retrieveFirst(){
retrieve = start;
}

//resets retrieve to the last node
void retrieveLast(){
cout << "retrieveLast() was called" << endl;

retrieve = start;
cout << "retrieve set to start" << endl;

cout << "start: " << start << endl;

cout << "retrieve: " << retrieve << endl;

while(retrieve->getNext()!=NULL){
retrieve = retrieve->getNext();
}
}

//sets retrieve to the next node,
//returns true if successful, false if not
bool retrieveNext(){
if(retrieve->getNext()!=NULL){
retrieve = retrieve->getNext();

cout << "retrieveNext() returns true" << endl;
return true;
}else{
cout << "retrieveNext() returns false" << endl;
return false;
}
}

//sets retrieve to the previous node,
//returns true if successful, false if not
bool retrievePrev(){
node* temp = retrieve;
if(temp==start){
return false;
}

while(temp->getNext()!=retrieve){
temp = temp->getNext();
}

retrieve = temp;
return true;
}

//returns the data of current retrieve
T retrieveData(){
return retrieve->getData();
}

//returns a pointer to the data of current retrieve
T* retrieveDataP(){
return retrieve->getDataP();
}

//returns the value of start(just for diagnostics)
T* retrieveStart(){
return start;
}
};//end of class LinkedList

//function prototypes
void readfile(LinkedList< LinkedList<string> > list);
void writefile(LinkedList< LinkedList<string> > list);

int main(int argc, char *argv[])
{
LinkedList< LinkedList<string> > list;

readfile(list);

cout << "==========================" << endl;

writefile(list);

system("PAUSE");
return EXIT_SUCCESS;
}

//file may not be empty
//report entry must end with newline character
void readfile( LinkedList<LinkedList<string> > list){
ifstream input;
input.open("reports.txt");
if(input.fail()){
cerr << "error, couldn't open 'reports.txt'" << endl;
}

string temp;

do{
LinkedList<string> wrapper;

getline(input, temp);
//if there are characters in the line
if(temp.length()>0){
//store lines in a wrapper
wrapper.store(temp);
}else{
//otherwise store wrapper in list
list.store(wrapper);
}
}while(!input.eof());

input.close();
}//readfile

void writefile(LinkedList< LinkedList<string> > list){
ofstream output;
output.open("reports output.txt");
if(output.fail()){
cerr << "error, couldn't open 'reports output.txt'" << endl;
}

cout << "file was successfully opened" << endl;
cout << "list.start: " << list.retrieveStart() << endl;

//set list's retrieve to Last
list.retrieveLast();
cout << "list.retrieveLast()" << endl;

//set wrapper's retrieve to First
list.retrieveDataP()->retrieveFirst();

//write first wrapper's data to file
output << list.retrieveDataP()->retrieveData() << endl;
while(list.retrieveDataP()->retrieveNext()){
output << list.retrieveDataP()->retrieveData() << endl;
}

//write an endline after the first wrapper;
output << "\n";

//write subsequent wrappers' data to file
while(list.retrievePrev()){
output << list.retrieveDataP()->retrieveData() << endl;
while(list.retrieveDataP()->retrieveNext()){
output << list.retrieveDataP()->retrieveData() << endl;
}
//write an endline after the wrapper
output << "\n";
}

output.close();
}//writefile

Just a question, but is it required that you do it this way? It seems that
a std::vector<std::vector<std::string> > and some judicious coding could do
this in about 1/10th the size of your program.
 
J

Jerry Coffin

[ ~300 lines elided... ]
Just a question, but is it required that you do it this way? It seems that
a std::vector<std::vector<std::string> > and some judicious coding could do
this in about 1/10th the size of your program.

Just a question, but is it required that you quote this way? It seems
that some judicious editing could do this in about 1/100th the size of
your post.

:)
 
J

Jim Langston

Jerry Coffin said:
[ ~300 lines elided... ]
Just a question, but is it required that you do it this way? It seems
that
a std::vector<std::vector<std::string> > and some judicious coding could
do
this in about 1/10th the size of your program.

Just a question, but is it required that you quote this way? It seems
that some judicious editing could do this in about 1/100th the size of
your post.

:)

Because if I do not quote what I am responding to, people in Google Groups
will not see what I'm responding to.

Even when I go to reply to a message after reading a thread and then see
that someone has snipped out code under discussion and I have to go back to
the OP and find the code and cut and paste, etc... Yes, it does make posts
longer, but unless something has been resolved where the code no longer
applies, it is customary to leave it in the reply.
 
J

Jerry Coffin

[ ... ]
Because if I do not quote what I am responding to, people in Google Groups
will not see what I'm responding to.

Nonsense. One of Google's few good points is that their users are pretty
much guaranteed access to the entire thread, even if it continues long
after normal newsreaders have discarded messages early in the thread.

You copied roughly 300 lines of code, but didn't comment directly on any
of it -- including it in your reply was 100% pointless.
 
B

BobR

Jim Langston said:
Because if I do not quote what I am responding to, people in Google Groups
will not see what I'm responding to.

Screw google groups! Why make me load the same thing ** over and over **
just because I want to follow a thread? That's NOT nice! Don't be so
selfish.

I'll bet your house looks like crap because you never take the garbage out!!
<G>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,928
Messages
2,570,068
Members
46,513
Latest member
JacklynMcC

Latest Threads

Top