简体   繁体   中英

C++ program crashes when using a copy-constructor, dynamic memory may be the culprit?

For this program, I've created a class called Map which acts like a linked-list by storing a struct called MapItem. What I've noticed is when I call the copy-constructor and pass in a Map object with no MapItems in it, the program does not crash. However, if I call the copy-construct and pass in a Map object with MapItems in it, the program crashes. As a result, I think the problem may lie with the add function I have attached below or maybe my destructor function since when I comment out the destructor function it works. Code is pasted below and kept as minimal as possible. Thanks.

Map.h

#include <iostream>
using namespace std;

template <class keyType, class valueType>
struct MapItem
{
    keyType key;
    valueType value;
    MapItem<keyType, valueType> *prev, *next;
};

template <class keyType, class valueType>
class Map
{
    public:
        Map();  // constructor for a new empty map
        Map (const Map<keyType, valueType> & other); //constructor for a new map that is intialized with the values of other
        ~Map (); // destructor
        void add (const keyType &key, const valueType &value); 
          /* Adds a new association between the given key and the given value.
             If the key already has an association, it should do nothing.
          */
private:
         /* The head and tail of the linked list you're using to store
            all the associations. */
         MapItem <keyType, valueType> *head, *tail;
         int sizeList; //size of the list
         int position; //key-value pair we are looking at, can be from 1 - sizeList using next() and first() function
};

Map.cpp

#include "Map.h"

template <class keyType, class valueType>
Map<keyType, valueType>::Map()
{
    sizeList = 0; //set the size to 0
    position = 1; //position is from 1 -> sizeList
    head = NULL; //head points to NULL
    tail = NULL; //tail points to NULL
}

template <class keyType, class valueType>
Map<keyType, valueType>::Map(const Map<keyType, valueType> &other) //copy constructor
{
    head = NULL;
    tail = NULL;
    sizeList = other.sizeList; //assign the same size (shallow copy)
    position = other.position; //assign the same position (shallow copy)
}

template <class keyType, class valueType>
Map<keyType, valueType>::~Map()
{
    struct MapItem<keyType, valueType> *temp; //create temp variable to hold which item we are looking at in the list
    temp = head; //start at the head

    for(int i = 1; i <=sizeList; i++)
    {
        MapItem<keyType, valueType> *next = temp->next;

        delete temp; //delete memory to pointed by temp

        if(i != sizeList) //if we are not at the last node
            temp = next;
    }
}

template <class keyType, class valueType>
void Map<keyType, valueType>::add(const keyType &key, const valueType &value)
{
    struct MapItem<keyType, valueType> *newItem; //create pointer to new map item

    if(sizeList == 0) //if linked list is empty, make newItem the HEAD
    {
        newItem = new MapItem<keyType, valueType>; //dynamically allocate a new item on the heap
        newItem->key = key; //assign the key
        newItem->value = value; //assign the value

        sizeList++; //increment size
        head = newItem; //set the HEAD of the list to newItem
        tail = newItem; //set the TAIL of the list to newItem
        newItem->prev = head; //previous item is the head (itself)
        newItem->next = head; //next item is the head (itself)
    }
    else //if the linked list is not empty, add it in
    {
        struct MapItem<keyType, valueType> *temp = head; //store the first element in the linked list in temp variable

        if(sizeList == 1) //if there is only one element in the list, check if they equal eachother
        {
            if(head->key != key)
            {
                newItem = new MapItem<keyType, valueType>; //dynamically allocate a new item on the heap
                newItem->key = key; //assign the key
                newItem->value = value; //assign the value

                tail = newItem; //assign newItem as the TAIL                
                head->next = tail; //assign the next of HEAD to the new map item
                head->prev = tail; //assign the previous of HEAD as the newItem (tail)
                tail->prev = head; //assign head to PREV of newItem (tail)
                tail->next = head; //assign HEAD to NEXT of newItem (tail)
                sizeList++; //increment size of list
            }   
        }
        else
        {
            bool sameKey = false; //boolean value to check if the same key already exists, and if it does it will stop the loop
            int i = 1; //which item we are looking at in the list

            while(i <= sizeList && !sameKey) //while not past the end of the list, keep checking if a similar key exists
            {
                if(temp->key == key)
                    sameKey = true;
                else
                {
                    temp = temp->next; //go to the next map item
                    i++;
                }
            }

            if(!sameKey) //if the same key has not been found
            {
                newItem = new MapItem<keyType, valueType>; //dynamically allocate a new item on the heap
                newItem->key = key; //assign the key
                newItem->value = value; //assign the value

                tail->next = newItem;
                newItem->prev = tail;
                newItem->next = head;
                tail = newItem;
                head->prev = tail;
                sizeList++;
            }
        }
    }
}

test.cpp

#include "Map.cpp"


int main()
{
    Map<int, int> b;
    b.add(1, 1); //if this line is commented out with the destructor intact, then the copy constructor call below works. if this line is NOT commented out with the destructor intact, the program crashes. 
    Map<int, int> a(b);

    system("PAUSE");
    return 0;
}

Your destructor trusts the member variables and attempts to delete that many items.

But your copy constructor leaves them inconsistent with the amount of data, since it never copies any actual data.

template <class keyType, class valueType>
Map<keyType, valueType>::Map(const Map<keyType, valueType> &other) //copy constructor
{
    head = NULL; // now there are zero items in the list
    tail = NULL;
    sizeList = other.sizeList; // this is a lie, really there are zero items
    position = other.position;
}

When you copy an empty list, you get sizeList set to the correct value by accident, so nothing fails.

a has head == NULL , so when in the destructor you do

temp = head; //start at the head
...
MapItem<keyType, valueType> *next = temp->next;

you are accessing a null pointer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM