main()函数的意外返回值

时间:2019-10-13 15:02:24

标签: c++ linked-list

我正在使用双向链表。每个函数运行良好,但是在main()的末尾,它停顿了几秒钟并返回了意外的随机值。

起初,我认为这是由report()函数引起的,因此我在末尾再添加了一个add()函数,但没有得到解决。我怀疑这是内存分配问题,但我看不到某些对象在哪里进行了预分配。 (使用Code :: Blocks 17.12编译)。

这是我的.cpp文件(全部合为一体):

#include <iostream>
using namespace std;

typedef struct element {
    element(){
        data = 0;
        next = 0;
        prev = 0;
    }
    ~element(){
        delete next;
        delete prev;
        cout << "element destructed" << endl;
    }
    int data;
    element* next;
    element* prev;
} elem;

typedef struct doublylinkedlist{
    doublylinkedlist(){
        head = 0; tail = 0;
    }
    ~doublylinkedlist(){
        while(head!=0) {
        head = head->next;
        delete head->prev;
    }
        delete tail;
        cout << "list destructed" << endl;
    }
    elem* head;
    elem* tail;
} doublyll;

doublyll ls;

void add(){
    elem* temp = new elem;
    cout << "Enter an integer: ";
    cin >> temp->data;

    if(ls.head == 0) {//empty
        ls.head = new elem;
        ls.head = temp;

    } else{
        if(ls.tail == 0){  //1-item list
            ls.tail = new elem;
            ls.tail = temp;
            ls.head->next = ls.tail;
            ls.tail->prev = ls.head;
        }
        else{
            temp->prev = ls.tail;
            ls.tail->next = temp;
            ls.tail = temp;
        }
    }
}

void report(){
    if(ls.head == 0) cout << "List is empty!" << endl;
    else{
        elem *temp = ls.head;
        do{
            cout << temp->data << endl;
            temp = temp->next;
        } while (temp != 0);
    }
}



int main(){
    report();
    add();
    add();
    add();
    report();
    add();
    return 0;
}

有人可以指出错误的出处以及如何解决?我希望main()不会像往常一样停滞并返回0,而不是相反。 这是the program when executed,这是我的build message

2 个答案:

答案 0 :(得分:0)

第一点:元素将由类doublylinkedlist释放,因此在类element中取消分配元素将导致双重释放。 因此,应该从delete的析构函数中删除两个element语句。

    ~element(){
        /* remove them */
        //delete next;
        //delete prev;
        cout << "element destructed" << endl;
    }

第二点:在doublylinkedlist的析构函数中,head->prevhead = head->next;之后被读取,而不检查head是否为NULLhead可以由任务分配NULL,因此应进行检查。

    ~doublylinkedlist(){
        while(head!=0) {
            head = head->next;
            if (head!=0) /* add this */
                delete head->prev;
        }
        delete tail;
        cout << "list destructed" << endl;
    }

最后一个元素将由delete tail;释放,因此此代码看起来很棘手,但应该可以。

加点:这些代码段

        ls.head = new elem;
        ls.head = temp;

            ls.tail = new elem;
            ls.tail = temp;

通过分配元素并将其立即丢弃而导致内存泄漏。 您应该删除多余的分配。

        /* remove this */
        //ls.head = new elem;
        ls.head = temp;

            /* remove this */
            //ls.tail = new elem;
            ls.tail = temp;

答案 1 :(得分:0)

除非您使用std::shared_ptr或类似的构造,否则每个对象都需要拥有另一个对象,该对象是它的所有者并负责取消分配它。您的代码需要具有清晰的语义来转移所有权(例如,函数createNode()希望其调用者破坏该节点)。

在您的代码节点中,列表和每个元素均将其删除。这意味着所有内容都会被删除两次(或更多)。在您的特定情况下,这是doublylinkedlist被破坏时的事件序列:

  1. doublylinkedlist删除其第一个元素。
  2. 第一个元素的析构函数将删除其前一个元素,该元素为null,因此无效
  3. 第一个元素的析构函数删除其下一个元素(第二个元素)。
  4. 第二个元素的析构函数删除其前一个元素(第一个元素)
  5. 第一个元素的析构函数将删除其前一个元素,该元素为null,因此无效
  6. 第一个元素的析构函数删除其下一个元素(第二个元素)。

此无限循环最终导致堆栈溢出。请注意,这不能保证是确切的事件顺序,因为两次删除对象是未定义的行为,因此有可能发生任何事情。

简单的解决方法是删除element析构函数,并让列表负责所有元素的生命周期。 您还应该修改doublylinkedlist析构函数,因为它将尝试取消引用最后一个元素上的空指针,并且您也不需要删除tail,因为它应该已经被删除了。例如:

~doublylinkedlist(){
    while(head!=0) {
        auto temp = head;
        head = head->next;
        delete temp;
    }
}

您还应确保遵守rule of three/five)。一种实现方法是利用智能指针,例如使用unique_ptr s来使代码看起来像这样:

#include <iostream>
#include <memory>
using namespace std;

typedef struct element {
    element() {
        data = 0;
        next = nullptr;
        prev = nullptr;
    }
    ~element() {
        cout << "element destructed" << endl;
    }
    int data;
    std::unique_ptr< element > next;
    element* prev;
} elem;

typedef struct doublylinkedlist {
    doublylinkedlist() {
        head = 0; tail = 0;
    }
    ~doublylinkedlist() {
        std::cout << "list destructed\n";
    }

    std::unique_ptr< elem > head;
    elem* tail;
} doublyll;

doublyll ls;

void add() {
    std::unique_ptr<elem> temp(new elem());
    cout << "Enter an integer: ";
    cin >> temp->data;

    if (ls.head == nullptr) {//empty
        ls.head = std::move(temp);
    }
    else {
        if (ls.tail == nullptr) {  //1-item list
            ls.head->next = std::move(temp);
            ls.tail = ls.head->next.get();
            ls.tail->prev = ls.head.get();
        }
        else {
            temp->prev = ls.tail;
            ls.tail->next = std::move(temp);
            ls.tail = ls.tail->next.get();
        }
    }
}

void report() {
    if (ls.head == 0) cout << "List is empty!" << endl;
    else {
        elem *temp = ls.head.get();
        do {
            cout << temp->data << endl;
            temp = temp->next.get();
        } while (temp != 0);
    }
}

int main() {
    report();
    add();
    add();
    add();
    report();
    add();
    return 0;
}

现在element的所有权是明确的,列表拥有头,head拥有其next节点,节点拥有其next节点,依此类推。销毁列表会自动破坏第一个节点自动销毁第二个节点等。在此代码中,您实际上可以完全省略析构函数。这也应该有助于防止内存泄漏,例如,如果您决定对add添加一些错误检查,则未使用的temp元素将被自动删除:

void add() {
    std::unique_ptr<elem> temp(new elem());
    cout << "Enter an integer: ";
    cin >> temp->data;
    if (!cin || temp->data > 100) {
       cout << "invalid input value\n";
       return; // temp is automatically deleted here
    }
    ...
}