且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

c++中动态分配的链表,异常后如何防止内存泄漏?

更新时间:2023-02-12 13:36:07

新节点"失败后抛出异常.

所以我必须在异常处理程序中显式调用析构函数.

没有.如果构造函数没有完成,则不能调用析构函数(对象从未创建).

我该如何处理这种情况以防止内存泄漏?

如果异常是在 new (std::bad_alloc) 中抛出的,那么你不需要做什么.

如果异常是从构造函数抛出的.然后每个完全构造的成员都会自动调用其析构函数.所以你不需要担心普通会员.如果您的任何成员是指针(已在构造函数中初始化),那么您需要确保它们被删除(这就是为什么您不希望对象中包含 RAW 指针(您需要智能指针)).>

这么说.您没有使用智能指针,但我看不到任何明显的泄漏.

但是您的类中拥有一个 RAW 指针.你还没有读过 3 的规则(查一查).目前因为你不遵守规则 3,下面的代码会崩溃.

void myCode(){链表列表1;list1.addFirst("马丁");链表 list2(list1);}

I like to implement linked list in c++ ,while adding new node I dynamically allocate it, if some allocation fails I would like my program to stop the execution.

After the "new Node" fails the exception is thrown so do I have to call the destructor explicitly in the exception handler. how can I deal with this situation in order to prevent memory leak? Here is my code that I have written

LinkedList.h

#pragma once
#include <iostream>
#include <string.h>
using namespace std;

class LinkedList
{
public:
    class Iterator; 
private:
    class Node
    {
        friend class Iterator;
        friend class LinkedList;
        char* m_word;
        Node *m_next;
        Node(const char* word,Node* next = NULL,Node* prev = NULL);
        ~Node();
    };
    Node *m_head,*m_tail;
    size_t m_num;
public:
    LinkedList():m_head(NULL),m_tail(NULL),m_num(0){};
    ~LinkedList();
    LinkedList& addFirst(const char* word);
    LinkedList& addLast(const char* word);
    Iterator erase(Iterator& it);
    Iterator begin();
    Iterator end();
    Iterator find(const char* word);
    size_t size()const{return m_num;};
    void print();

    friend class Iterator;
    class Iterator
    {
        LinkedList& m_list;
        Node *m_prev,*m_cur;
        friend class LinkedList;
        void next();
        void setLast(){m_cur = NULL,m_prev = m_list.m_tail;}
    public:
        Iterator(LinkedList& linkedList):m_list(linkedList),m_prev(NULL),m_cur(linkedList.m_head){}
        Iterator& operator++();
        char* operator*();
        bool operator != (Iterator& it){return (m_cur != it.m_cur || m_prev != it.m_prev);}
    };
};

LinkedList.cpp

#include "LinkedList.h"


LinkedList::Node::Node(const char* word,LinkedList::Node* prev,LinkedList::Node *next)
{
    char* tmpWord = new char[strlen(word)+1];
    strcpy(tmpWord,word);
    m_word = tmpWord;
    m_next = next;
    if(prev != NULL)
        prev->m_next = this;
}
LinkedList::Node::~Node()
{
    delete[] m_word;
}

LinkedList::~LinkedList(void)
{
    for(Iterator it = begin();it != end();)
        erase(it);
}
LinkedList& LinkedList::addFirst(const char* word)
{
    Node* node = new Node(word,NULL,m_head);
    m_head = node;
    if(m_tail == NULL)
        m_tail = m_head;
    ++m_num;
    return *this;
}
LinkedList& LinkedList::addLast(const char*word)
{
    if(m_head == NULL)
        addFirst(word);
    else
    {
        Node* node = new Node(word,m_tail,NULL);
        m_tail = node;
    }
    ++m_num;
    return *this;
}
LinkedList::Iterator LinkedList::begin()
{
    Iterator it(*this);
    return it;
}
LinkedList::Iterator LinkedList::end()
{
    Iterator it(*this);
    it.setLast();
    return it;
}
LinkedList::Iterator LinkedList::erase(LinkedList::Iterator& it)
{
    if(it.m_cur != NULL)
    {
        Node* tmp = it.m_cur;
        if(it.m_prev != NULL)
            it.m_cur = it.m_prev->m_next = tmp->m_next;
        else
            it.m_cur = it.m_list.m_head = tmp->m_next;
        if(tmp == it.m_list.m_tail)
            it.m_list.m_tail = NULL;
        delete tmp;
        --m_num;
    }
    return it;
}
LinkedList::Iterator LinkedList::find(const char* word)
{
    Iterator it = begin();
    for(;it != end();++it)
    {
        if(!strcmp(it.m_cur->m_word,word))
            break;
    }
    return it;
}

void LinkedList::Iterator::next()
{
    if(m_cur != NULL)
    {
        m_prev = m_cur;
        m_cur = m_cur->m_next;
    }
    else
        m_prev = NULL;
    return;
}
void LinkedList::print()
{
    for(Iterator it = begin();it !=end();++it)
        cout << it.m_cur->m_word;
}
LinkedList::Iterator& LinkedList::Iterator::operator ++()
{
    next();
    return *this;
}
char* LinkedList::Iterator::operator *()
{
    return m_cur->m_word;
}

//int main()
//{
//  LinkedList ll;
//  ll.addFirst("1");
//  ll.addFirst("2");
//  ll.addLast("3");
//  ll.addLast("4");
//  LinkedList::Iterator it = ll.find("5");
//  return 0;
//}

After the "new Node" fails the exception is thrown.

so do I have to call the destructor explicitly in the exception handler.

No. If the constructor did not finish then you can not call the destructor (the object was never created).

how can I deal with this situation in order to prevent memory leak?

If the exception is thrown in new (std::bad_alloc) then there is nothing you need to do.

If the exception is thrown from the constructor. Then every fully constructed member will have its destructor called automatically. So you don't need to worry about normal members. If any of your members are pointers (that have been initialized in the constructor) then you need to make sure that these get deleted (which is why you don't want RAW pointers in your object (you want smart pointers)).

Saying that. You do not use smart pointers but I can't see any obvious leaks.

But you have a RAW pointer in your class that is owned. You have not read about the rule of 3 (look it up). Currently because you do not obey the rule of 3 the follwoing code will crash.

void myCode()
{
    LinkedList    list1;
    list1.addFirst("Martin");

    LinkedList    list2(list1);
}