Search code examples
c++explicit-destructor-call

C++ unintended destructor calls


I have a homework about data structures in C++ but I have encountered with a strange problem. Sorry if the title is a bit uninforming. First of all,for the homework, we've given a PaperRepository class' header file to implement. This class holds the information of papers via a circular doubly linked list. Every paper has title, a journal in which the paper is published, year of publication and a list of co authors which is hold by another linked list class(sorted linked list), Here is the PaperRepository.h file

// PaperRepository.h

#include <string>

#include "CDoublyLinkedList.cpp"
#include "Paper.cpp"

using std::string;

class PaperRepository
{
public:
    PaperRepository();
    ~PaperRepository();

    void addPaper( string paperTitle, string journalTitle,int publicationYear );
    void removePaper( string paperTitle );
    void addCoauthor( string paperTitle, string coauthorFirstName,string coauthorLastName, string coauthorInstitution);
    void removeCoauthor ( string coauthorFirstName, string coauthorLastName);
    void showAllPapers();
    void showCoauthor( string coauthorFirstName, string coauthorLastName );
    void showJournal( string journalTitle );

private:
    CDoublyLinkedList<Paper> papers;
};

To hold papers, I have implemented a circular doubly linked list class(as my instructor said). Here is the CDoublyLinkedList.cpp

// CDoublyLinkedList.cpp

#include <cstdlib>
#include <iostream>

template <class T> class CDoublyLinkedList
{
private:
    struct Node
    {
        T data;
        Node* prev,*next;
        Node(const Node& other)
        {
            other.data = data;
            other.prev = prev;
            other.next = next;
        }

        Node(T data)
        {
            this->data = data;
            prev = NULL;
            next = NULL;
        }
    };

    Node* head;
    int listSize;

public:
    CDoublyLinkedList()
    {
        head = NULL;
        listSize = 0;
    }

    ~CDoublyLinkedList()
    {
        std::cout<<"CDoublyLinked List's destructor is called."<<std::endl;
        Node* cur = head;

        for(int ctr = 0;ctr < listSize ; ctr++)
        {
            head = cur->next;
            delete cur;
            cur = head;
        }
    }

    void addToBeginning(T data)
    {
        Node* newNode = new Node(data);
        std::cout<<"inside add to beginning"<<std::endl;

        if(listSize == 0)
        {
            newNode->next = NULL;
            newNode->prev = NULL;
            head = newNode;
            listSize++;
            return;
        }
        if(listSize == 1)
        {
            newNode->prev = head;
            newNode->next = head;
            head->prev = newNode;
            head->next = newNode;
            head = newNode;
            listSize++;
            return;
        }

        newNode->next = head;
        newNode->prev = head->prev;
        head->prev->next = newNode;
        head->prev = newNode;
        head = newNode;
        listSize++;
    }

    void addToEnd(T data)
    {
        Node* newNode = new Node(data);

        //newNode->data = data;
        if(listSize == 0)
        {
            newNode->next = NULL;
            newNode->prev = NULL;
            head = newNode;
            listSize++;
            return;
        }
        if(listSize == 1)
        {
            newNode->next = head;
            newNode->prev = head;
            head->next = newNode;
            head->prev = newNode;
            listSize++;
            return;
        }

        newNode->next = head;
        newNode->prev = head->prev;
        head->prev->next = newNode;
        head->prev = newNode;
        listSize++;
    }

    void add(T data)
    {
        std::cout<<"Inside CDoublyLinkedList add."<<std::endl;
        if(listSize == 0)
        {
            addToBeginning(data);
            std::cout<<"After adding to the beginning"<<std::endl;
        }
    else
        addToEnd(data);
    }

    void clearList()
    {
        Node* cur = head;
        for(int ctr = 0;ctr < listSize ; ctr++)
        {
            head = cur->next;
            delete cur;
            cur = head;
        }

        listSize = 0;
        head = NULL;
    }
};

Here is the LinkedList.cpp. Since I do not add or remove any co-author to the list, I don't write the add and remove methods in here. LinkedList.cpp

// LinkedList.cpp

#include <iostream>
#include <cstdlib>

template <class T> class LinkedList
{
private:
    struct Node
    {
        T data;
        Node *next;
        Node(const Node& other)
        {
            other.data = data;
            other.next = next;
        }
        Node(T data)
        {
            this->data = data;
            next = NULL;
        }
    };

    Node *header;
    int listSize;

public:
    LinkedList()
    {
        std::cout<<"LinkedList's constructor is called."<<std::endl;
        header = NULL;
        listSize = 0;
    }
    ~LinkedList()
    {
        std::cout<<"Linked List destructor is called"<<std::endl;
        Node* temp;
        while(header)
        {
            temp = header;
            header = header -> next;
            delete temp;
        }
    }

And my implementation of the add method of the PaperRepository class is here: PaperRepository.cpp

// PaperRepository.cpp

#include <cstdlib>
#include "PaperRepository.h"
#include <iostream>

PaperRepository::PaperRepository()
{
}

PaperRepository::~PaperRepository()
{
    papers.clearList();
}

void PaperRepository::addPaper( string paperTitle, string journalTitle,int
publicationYear )
{
    std::cout<<"add paper is called."<<std::endl;
    Paper newPaper(paperTitle,journalTitle,publicationYear);
    std::cout<<"new paper is created."<<std::endl;
    std::cout<<"before adding paper."<<std::endl;
    papers.add(newPaper);
    std::cout<<"after adding paper."<<std::endl;
}

I didn't add other methods because my problem starts at add method.Lastly, there are the important parts of my Paper and CoAuthor classes. Paper.cpp

// Paper.cpp

#include <string>
#include <iostream>
#include "LinkedList.cpp"
#include "CoAuthor.cpp"

using std::string;

class Paper
{
private:
    string title;
    string journal;
    int year;
    LinkedList<CoAuthor> coAuthors;

public:
    Paper()
    {
        title = "";
        journal = "";
        year = 0;
    }

    Paper(string title, string journal, int year)
    {
        this->title = title;
        this->journal = journal;
        this->year = year;
    }

    ~Paper()
    {
        coAuthors.clearList();
    }
};

And CoAuthor.cpp

// CoAuthor.cpp

#include <iostream>
#include <string>

using std::string;

class CoAuthor
{
private:
    string name,lastName,institution;

public:
    CoAuthor(string name,string lastName, string institution)
    {
        this->name = name;
        this->lastName = lastName;
        this->institution = institution;
    }

    CoAuthor()
    {
        name = "";
        lastName = "";
        institution = "";
    }
};

And this is the file I'm currently testing. main.cpp

// main.cpp

#include "PaperRepository.h"
#include <iostream>
#include <string>

int main()
{
    PaperRepository myRep;
    myRep.addPaper("MyTitle","Journal",1950);
    std::cout<<"End of main..."<<std::endl;

    return 0;
}

When I run the program, I see that at the time the command in CDoublyLinkedList.cpp file's add method is called and "Node newNode = new Node(data)" is executed, the LinkedList class' destructor is called. This is very strange to me.Moreover, the LinkedList class' destructor is called 5 times in total. I am adding data to circular doubly linked list class and it invokes the other linked list class' destructor. Sorry, this is a bit long but I cannot explain myself otherwise. Here is the output with this main.cpp file:

>add paper is called.
> *LinkedList's constrctor is called.
> *new paper is created.
> before adding paper
> Inside Doubly Linked List's add
> LinkedList's constructor is called.
> LinkedList's destructor is called.
> inside addToBeginning.
> LinkedList's destructor is called.
> After addToBeginning method
> LinkedList's destructor is called.
> after adding paper
> LinkedList's destructor is called.
> End of main...
> Linked List's destrutor is called.
> CDoublyLinkedList's destructor is called.

Solution

  • You are adding values to your linked lists, not pointers. First, you instantiate (for example) a new Paper object on the stack. Then, you add() it to the linked list, passing the object by value, which copies the object. Then the function scope ends, and the local Paper object is destroyed.

    You also have various other typos and basic problems with the classes. For example:

    struct Node{
        T data;
        Node *next;
        Node(const Node& other){
            other.data = data;
            other.next = next;
        }
    ...
    }
    

    That shouldn't even compile. It's supposed to be a copy constructor that sets the members to the values supplied by the other input parameter, not the other way around. That parameter is const so that you can't modify it, which is why it shouldn't compile.

    In general, I recommend a brief session with your instructor to discuss your code.