C ++ Деструктор List вызывается после каждого использования метода List - PullRequest
1 голос
/ 19 февраля 2011

Это продолжение вопроса, который я задал ранее сегодня относительно назначения прямого вызова деструктора класса.

Я создаю свой собственный Список для назначения.Я перегрузил назначение, квадратные скобки, входящие и исходящие потоковые операторы и имею базовые функции добавления, удаления и печати на экран.

Мой следующий шаг - удаление списка и освобождение памяти всех его функций.узлы, с которыми у меня возникают проблемы.

У меня есть список, записывающий на экран свое текущее состояние после выполнения своих операций, следующим образом:

void main()
{
    // Create instance of list
    List aList(true);
    List bList(true);
    aList.Add(1);
    aList.Add(2);
    bList.Add(2);
    cout << "aList: " << aList;
    cout << "bList: " << bList;
    aList = bList;
    cout << "aList: " << aList;
    cout << "bList: " << bList;
    aList.Add(3);
    cout << "aList: " << aList;
    cout << "bList: " << bList;
    int i = 0; cin >> i;
}

Вот мой перегруженный выходной поток:

ostream &operator<<(ostream &stream, List theList)
{
    stream << "There are " << theList._totalNodes << " nodes in the list." << endl;
    for(int i = 0; i < theList._totalNodes; i++)
    {
        stream << "Node #" << i << " holds the data: " << theList[i] << endl;
    }
    return stream;
}

Когда вы используете это окно командной строки, под ним также напечатано "Destructor call".

Деструктор списка:

List::~List()
{
    cout << "Destructor called" << endl;
//  List::Destroy();
}

Перегруженоператор присваивания также, кажется, вызывает деструктор, что меня не удивляет.Однако другие делают.

List &List::operator=(List &aList)
{
    // Make sure not the same object trying to be copied
    if(this != &aList)
    {
        // Perform deep copy
        // Delete old pointers in redundant list, only if they exist already
        if(_head)
        {
            delete _head, _tail, _current, _del;
            _totalNodes = 0; // Reset total nodes
        }
        // Now set new pointers copied from other list
        _head = NULL;
        _tail = _head;
        _current = _head;
        _del = _head;
        // Now loop through all nodes in other list and copy them to this
        // Reset pointer in other list
        aList.Reset();
        for(int i = 0; i < aList._totalNodes; i++)
        {
            Add(aList.Current()->_data);
            aList.Next();
        }
        // Reset other list's pointer
        aList.Reset();
        Reset(); // Reset our pointer
    }
    return *this; // Return for multi assignment
}

У меня также есть деструктор для записи списка «Разрушитель вызван», когда он вызывается, и заметил, что он вызывается после каждого использования одного из его методов.

Почему это?Я предположил, что деструктор вызывается, когда объект больше не нужен, то есть удаляется.

Кроме того, при переходе по моему коду я заметил, что при удалении указателя адрес памяти, на который он указывает, не будетаннулирована.Нужно ли вручную обнулять указатель после его удаления?

Ответы [ 2 ]

5 голосов
/ 19 февраля 2011

Это потому, что вы взяли свой Список по значению, то есть скопировали его.Вы должны взять его по константной ссылке, то есть

ostream &operator<<(ostream &stream, const List& theList)

Кроме того, ваш оператор присваивания должен использовать константную ссылку, а не неконстантную ссылку.

Я также не думаю, что это

delete _head, _tail, _current, _del;

делает то, что вы думаете, что делает.Он просто удаляет _del и ничего другого.

Вы должны использовать рекурсивно самодостаточный дизайн, где каждый узел удаляет следующий в цепочке.Изменить: Поскольку это было неясно, я собираюсь опубликовать краткий пример.

template<typename T> struct Node {
    Node(const T& arg, Node* prev) : t(arg), previous(prev) {}
    T t;
    std::auto_ptr<Node<T>> next;
    Node<T>* previous;
};
template<typename T> class List {
    std::auto_ptr<Node<T>> head;
    Node<T>* tail;
public:
    List() : head(NULL), tail(NULL) {}
    void clear() { head = tail = NULL; } // Notice how simple this is
    void push_back() {
        push_back(T());
    }
    void push_back(const T& t) {
        if (tail) {
            tail = (tail->next = new Node<T>(t, tail)).get();
            return;
        }
        tail = (head = new Node<T>(t, NULL)).get();
    }
    void pop_back() {
        tail = tail->previous;
        tail->next = NULL;
    }
    void pop_front() {
        head = head->next;
    }
    List<T>& operator=(const List& ref) {
        clear(); // Clear the existing list
        // Copy. Gonna leave this part to you.
    }
};

Обратите внимание, что я никогда ничего не удалял - std::auto_ptr сделал все для меня, и манипулирование списком и его узлами стало многопроще, потому что std::auto_ptr управляет всей задействованной памятью.Этот список даже не нуждается в собственном деструкторе.

Я предлагаю вам внимательно прочитать std::auto_ptr, в его интерфейсе есть некоторые сюрпризы, такие как конструктор "копирования" и оператор присваивания фактически перемещаются, чтовы захотите узнать о том, прежде чем использовать класс.Я хотел бы начать использовать std::unique_ptr вместо этого все время, что сделало бы жизнь намного проще.

1 голос
/ 19 февраля 2011

Ваша operator << перегрузка принимает List по значению . Это требует создания копии (для создания локальной переменной), и эта локальная копия уничтожается в конце функции перегрузки. Вот почему вы видите, что деструктор вызывается снова и снова.

Подумайте об изменении вашей перегрузки, чтобы вместо нее взять const List &. & означает, что он передается по ссылке, поэтому копия не создается. const означает, что функция гарантирует не изменять объект.

...