0

I am trying to dig into data structures in C++. Therefore I am learning how to write a list. Everything seemed to be working just fine until it came to overloading sum operator +. For two given lists it sums two of the highest values of the lists.

Here is .h file:

typedef struct rob{
    int value;
    struct rob* next;
}element;

class list{
public:
    friend list& operator+(list&,list&);
    friend void merge(list& x,list& y);

    void show();
    bool search(int x);
    void append(int x);
    bool sortAppend(int x);
    list& operator--(int);

    bool empty() { return (inf.head==nullptr);}
    void clear() { inf.head = nullptr; }
    list() { inf.head = inf.tail = nullptr; }
    ~list() { while(!empty()) { (*this)--;}}

private:
    typedef struct{
        element* head;
        element* tail;
    }info;

    info inf;
};

I know that in an .h file the typedef may seem a bit C-like, but the header design is copied from the book that I am learning from. I am trying to crack the methods by my own though using authors ideas.


And relevant function definitions:

#include "list.h"
bool list::sortAppend(int x){

element* newElem = new element;
newElem->value = x;
if (empty()){
    inf.head=inf.tail=newElem;
    newElem->next=nullptr;
    return true;
}
else if ( (newElem->value) < (inf.head->value) ){ 
    newElem->next=inf.head;
    inf.head=newElem;
    return true;
}
else if ( (newElem->value) > (inf.tail->value) ) {
    newElem->next=nullptr;
    inf.tail->next=newElem;
    return true;
}
element* tempHead = inf.head;
while(tempHead!=inf.tail){

    if ( (newElem->value) < (tempHead->next)->value) {
           newElem->next = (tempHead->next);
           tempHead->next = newElem;
           return true;
    }
    else{
    tempHead = tempHead->next;
    }   
}
return false;
}

list& operator+(list& X, list& Y){
    list* tempListArr[2] = {&X, &Y};
    list* tempList = new list;
    for(const list* i: tempListArr)
    {
        element* tempHead = (i->inf).head;
        while(tempHead!= nullptr){
            tempList->sortAppend(tempHead->value);
            tempHead = tempHead->next;
            }   
            tempList->show();
            std::cout << "--\n";
    }
    return *tempList;
}

For given list containing values:

#include <iostream>
#include "list.cpp"

int main(){

    list myList;
    myList.sortAppend(5);
    myList.sortAppend(2);
    myList.sortAppend(4);
    list myList2;
    myList2.sortAppend(21);
    list myList3;

    myList3 = myList + myList2;

    return 0;

}

Could anyone point me where I made a mistake? I am stuck for a few hours now and I don't know what goes wrong.

Many thanks in advance!


FOLLOW UP:

The sortAppend method surely works. It does create a sorted list as desired. There must have been something wrong with the + operator definition itself though I have tried, instead of range loop, using for loop for one iteration and still I got a list of two values only.

hikamare
  • 304
  • 1
  • 14
  • 1
    You should do some line-by-line debugging to narrow down the problem. – Oliver Charlesworth Jul 05 '17 at 08:47
  • A couple of unrelated problems with your `clear` function. It will leak memory; And it doesn't "clear" the tail pointer. – Some programmer dude Jul 05 '17 at 08:49
  • 1
    You also have a memory leak in your `operator+` function. Please see e.g. [this binary operator canonical implementation reference](http://en.cppreference.com/w/cpp/language/operators#Binary_arithmetic_operators) for how to write non-member operator functions like `operator+`. – Some programmer dude Jul 05 '17 at 08:50
  • I am sure that sortAppend works fine while List1 is created by such method. I also checked that while adding List1 to List3 in first iteration each value gets into tempList but eventually that maximum capacity of that list is 2 records (I don't even know why while it is the same list as List1 which has 3 records and can have more). – hikamare Jul 05 '17 at 08:51
  • Don't use `typedef struct` in C++. It is only needed in C. –  Jul 05 '17 at 08:53
  • What does `sortAppend` do to a tail of the new element on a non-empty list? – doctorlove Jul 05 '17 at 08:53
  • You need to add more information to be able to reproduce this - does append work? Does making a list with 5, 2, and 4 and then showing it work? – doctorlove Jul 05 '17 at 09:04
  • 2
    There is a dangerous thing here: operator+ returns a reference to a heap object which has no owner. If this is fixed (i.e. by returning a local object instead) the assignment `myList3 = ...` will no longer work as there is no copy constructor and it only copies the pointers which will be freed as soon as the local object runs out of scope. – Andreas H. Jul 05 '17 at 09:11
  • And remind me what you want myList3 to contain? – doctorlove Jul 05 '17 at 09:14
  • You said "For two given lists it sums two of the highest values of the lists." for {5, 2, 4} and {21} what doyou expect back? – doctorlove Jul 05 '17 at 09:23
  • @AndreasH. It has been pointed out before. I noted that and I will change it when I solve the sum operator problem. – hikamare Jul 05 '17 at 09:34
  • @doctorlove I wish I had a sum of myList and myList2, so in a given example: {2,4,5,21}. – hikamare Jul 05 '17 at 09:36
  • Perhaps you want to find another book to learn from. – n. m. could be an AI Jul 05 '17 at 09:46
  • @hikamare -- None of this will work correctly without implementing correct copy semantics for your `list` class. You completely missed the chapter(s) in your book discussing the copy constructor and assignment operator. For starters [you need to implement the rule of 3](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – PaulMcKenzie Jul 05 '17 at 09:50
  • @PaulMcKenzie; the purpose of that book is to learn about algorithms and data structures in general. Therefore I believe there might be mistakes containing leakages or etc. I see that book as the example how things work, how stuff was build, I do not intend to follow that exact way of thinking about it in my real-life projects. It is, of course good to know that there are minor or major mistakes in it but my main goal is to learn how stuff works, from the scratch (just to know that appending to list is switching pointer etc). Thank for the feedback though :) Most important we found the bug :) – hikamare Jul 05 '17 at 11:05
  • I am not sure if you have seen my answer down there. IMHO it's exactly what you've been asking for. – Andreas H. Jul 05 '17 at 11:40
  • 1
    @hikamare -- The issue with using C++ to learn the basic data structures by reading a data structures book is that you can't simply use the algorithms from the book without creating the proper foundation first. That foundation is learning proper memory management, implementing correct copy semantics, etc. The solution to your problem is to properly implement the "rule of 3", by writing a user-defined copy constructor and assignment operator, **then** implement `operator `+`-- anything else is a hack. – PaulMcKenzie Jul 06 '17 at 10:05

2 Answers2

1

You are simply not setting inf.tail to the new tail in

else if ((newElem->value) > (inf.tail->value)) {
    newElem->next = nullptr;
    inf.tail->next = newElem;
    inf.tail = newElem; // <-- missing!
    return true;
}

You should - at least - change the signature of operator+ to return a list instead of a list reference and return a local object instead of an unowned heap object (which is a memory leak). If you do so you will have to write a copy constructor and copy assignment operator too.

Andreas H.
  • 1,757
  • 12
  • 24
1

Given your code,

list myListWierd;
myListWierd.sortAppend(2);
myListWierd.sortAppend(4);
myListWierd.sortAppend(5);
myListWierd.show();

shows

2
5

so the sortAppend does not work.

The trouble is around updating either the tail, since operator + relies on using the tail.

I could sort the code out for you and make it work; indeed Andreas' answer does this. But for now, notice you have assumed a function works, but I found a case it doesn't work for by looking at the moving parts - a list we created, that we then try to re-create in a different order. As a general rule, try all the parts in a function that goes wrong, one at a time, maybe as a unit test.

Rather than fixing this, for now, let's make a couple of suggestions.

First, the destructor does nothing, other than walk pointers (using empty which uses head and not tail - so head needs setting as said before)

~list() { while (!empty()) { (*this)--; } }

If you don't want leaks you need to give this more thought.

Next,

list& operator+(list&, list&)

creates a pointer and returns its contents. This is a BAD IDEA. NEVER DO THIS.

For now, change the signature to

list operator+(list&, list&);

and just return a list:

list operator+(list& X, list& Y) {
    list* tempListArr[2] = { &X, &Y };
    list tempList;//copy it over to the calling vode otherwise DANGER
    for (const list* i : tempListArr)
    {
        element* tempHead = (i->inf).head;
        while (tempHead != nullptr) {
            tempList.sortAppend(tempHead->value);
            std::cout << "Adding " << tempHead->value << '\n';
            tempHead = tempHead->next;
        }
        tempList.show();
        std::cout << "--\n";
    }
    return tempList;
}
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
doctorlove
  • 18,872
  • 2
  • 46
  • 62