-3

I'm trying to make a phone book, and I store my data in a linked list. However, when I try to delete it, I either get memory leak or some ugly exit status with some value from the memory. I guess this exit status signals some problems during the deletion of my list, because the program won't proceed to the next line.

I've tried messing with the destructors back and forth, but it seems I'm unable to solve this on my own.

I have three important classes. One for the Phonebook, one for a Contact, and one for a Data member of a Contact. The Data class is abstract, and has a few derived classes. Also I have a linked list struct which I use to store Contacts in the Phonebook, and to store Data in a Contact.

The linked list struct:

#include "Data.h"
class Contact; //to avoid an "undefined reference" error

struct ListMem{
    ListMem* next=NULL;
    Contact* cont=NULL;
    Data* dat=NULL;
}; //this class can't have a destructor that calls "delete cont",
//because of the order I declared my classes in

The Phonebook class:

#include "Contact.h"
#include "ListMem.h"

class Phonebook{
    size_t cont_num; //how many contacts are already stored
    ListMem* cont; //pointer to the first member of the linked list
public:
    Phonebook(const char* filename){ //reads the Phonebook from file
        std::ifstream is;
        is.open(filename);
        size_t pb_size;
        string line;
        ListMem* tmp;
        is>>pb_size; getline(is, line);
        cont_num=0;

        for(size_t i=0; i<pb_size; i++){
            getline(is, line);
            if(i==0){
                Contact* tmp_ct=new Contact(line);
                cont=add_cont(tmp_ct);
                tmp=cont;
            }
            else {
                Contact* tmp_ct=new Contact(line);
                tmp->next=add_cont(tmp_ct);
                tmp=tmp->next;
            }
        }
    }

    ListMem* add_cont(Contact* new_ct){ //adds a new contact
        ListMem* tmp=new ListMem;
        tmp->cont=new Contact(new_ct);
        cont_num++;
        return tmp;
    }

    ~Phonebook(){
        ListMem* tmp=cont;
        while(tmp!=NULL){
            ListMem* del=tmp;
            tmp=tmp->next;
            delete del->dat;
            delete del->cont;
            delete del;
        }
    }
};

The Contact class:

#include "Name.h"
#include "ListMem.h"

class Contact{
    size_t data_num; //number of data stored
    ListMem* data; //pointer to the first data member
public:
    Contact(string line){ //converts a "line" into a Contact
        ListMem* tmp;
        int dat_count=std::count(line.begin(), line.end(), ':');
        data_num=dat_count;
        string dat, tmp_dat, tmp_type;
        int pos_1=0, pos_2=line.find(';');

        for(int i=0;i<dat_count;i++){
            dat=line.substr(pos_1, pos_2-pos_1);
            tmp_type=dat.at(0);
            tmp_dat=dat.substr(2, dat.size()-2);
            pos_1+=dat.size()+1;
            pos_2=line.find(';', pos_1);
            if(i==0){
                data=add_data(tmp_type, tmp_dat);
                tmp=data;
            }
            else {
                tmp->next=add_data(tmp_type, tmp_dat);
                tmp=tmp->next;
            }
        }
    }

    ListMem* add_data(string type, string data){ //adds a new data member
            ListMem* tmp=new ListMem;
            if(type=="1") tmp->dat=new Name(data);
            //I have more, but it's irrelevant.
            data_num++;
            return tmp;
    }

    ~Contact(){
        ListMem* tmp=data;
        while(tmp!=NULL){
            ListMem* del=tmp;
            tmp=tmp->next;
            delete del->dat;
            delete del->cont;
            delete del;
        }
    }
};

The Data class:

class Data{
    string type;
public:
    Data(){}
    void set_type(string t) {type=t;}
    virtual ~Data(){}
};

One of the derived classes, the others look basically the same:

#include "Data.h"

class Name: public Data{
    string name;
public:
    Name(string n): name(n){ set_type("1");}
    ~Name(){}
};

And of course the main:

#include "Phonebook.h"

int main(){
    Phonebook* Test=new Phonebook("test.txt");
    delete Test;

    return 0;
}

In the 'test.txt' file, I have my test phone book:

3
1:test_name_1;
1:test_name_2;
1:test_name_3;

So when I run this code, I have memory leak, and I don't know why. I made the destructor run along the linked list and deallocate the dynamic memory, or at least that's what I thought.

I know I don't deallocate the Contacts the way I should, because I used a memory leaking checker tool, and a pattern repeats the same times the Contacts I have.

I used .cpp files, but I integrated the relevant parts here to save some space. Also I didn't put here the includes other than my project system.

I strongly suspect that the problem is with the destructors, either ~Phonebook(), ~Contact() or both. I put here the other parts because I've been told to post a version anyone can replicate without much trouble. I feel like there must be a stupid error and a simple solution, but I don't know what.

Can you guys help me out a bit?

Heves
  • 5
  • 3
  • 2
    "when I try to delete it, I either get memory leak or some ugly exit status with some value from the memory." Why didn't you post those?. Anyway in `Phonebook:add_cont` the lines `ListMem* tmp=new ListMem; tmp->cont=new Contact(new_ct);` look suspicious. How does this even compile? – Quimby May 19 '19 at 21:51
  • 1
    A [mcve] would be easier to diagnose. As a bonus, the process of creating a minimal, yet complete, example might show you where the error is. (To be complete, the example should not depend on a data file. Try creating an example with hard-coded dummy data instead of reading from a file.) – JaMiT May 19 '19 at 22:53
  • @Quimby Sorry I didn't think they could be useful in any way. I used the memory tracing program that the school supplied, so I'm not sure if the output is standard. Next time I'll definitely include them. And yes, that was the problem, thank you. But why do you ask how does this even comply? The compiler should notice things like this? – Heves May 20 '19 at 12:50
  • @Heves Sadly it usually doesn't/can't, but my point was that `new Contact(new_ct)` calls constructor `Contact::Contact(Contact* ptr)` and there isn't one. Had it been `Contact(*new_ct)` then a copy ctor would be called, which exists. Is that just a typo? If not then the compiler should complain. Other than that, posting error messages can do no harm and usually they are helpful. E.g. `valgrind` can usually at least point to the allocation code that created the leaked object. But glad to see the problem is resolved :). – Quimby May 20 '19 at 13:27
  • Also there's a difference between `new ListMem;` and `new ListMem()`; See https://stackoverflow.com/questions/620137/do-the-parentheses-after-the-type-name-make-a-difference-with-new . – Quimby May 20 '19 at 13:30

1 Answers1

0

Phonebook constructor parses the file, creates Contact object using new operator and pass it to add_cont(). Member function add_cont() creates a copy of Contact object passed as parameter again using new operator but never deletes origin. From my point of view within add_cont() 'new_ct' just could be assigned to 'tmp->cont'.

BTW a better approach would be to use available container classes like std::list, std::vector etc. and avoid raw pointers.

Mathias Schmid
  • 431
  • 4
  • 7
  • Thanks for your answer, you were right. I do this project for school, and they told us not to use STL containers, that's why I play around with linked lists. We only learn basic C++, they don't really teach us the newest features, or C++11 or anything about "not-raw" pointers. This is arguably useful in real life, but this is how school is. Thank you again. – Heves May 20 '19 at 12:46