【问题标题】:Why class destructor called if I delete linked list element?如果我删除链表元素,为什么会调用类析构函数?
【发布时间】:2021-08-11 17:52:37
【问题描述】:

我想通过在程序结束时删除所有节点来释放内存,但我也有删除特定节点的功能(重载运算符)。如果我要删除特定的节点类,则调用析构函数。谁能解释一下为什么,以及如何解决它。

类声明

class StudentList
{
    private:
        typedef struct student_node
        {
            student_node* prevSt;
            
        //######Student DATA######
            string surname;
            string name;
            string father_name;
            Date birthday;
            int year;
            string faculty;
            string departament;
            string group;
            string ID_number;
            string sex; 
        //########################
            SessionList session_data;
            int session_count;
        //########################
        
            student_node* nextSt;       
        }* student_nodePtr;
    
        student_nodePtr headSt;
        student_nodePtr currSt;
        student_nodePtr tailSt;
        student_nodePtr tempSt;
            
    public:
        StudentList();
        ~StudentList();
        StudentList operator-(student_nodePtr selectedSt);
};

构造函数、析构函数和重载运算符

StudentList::StudentList()
{
    headSt = NULL;
    currSt = NULL;
    tailSt = NULL;
    tempSt = NULL;
}

StudentList::~StudentList()
{
    cout << "What!?" << endl;
}

StudentList StudentList::operator-(student_nodePtr selectedSt)
{
    if(headSt == NULL || selectedSt == NULL)
    {
        return *this;
    }
    
    if(headSt == selectedSt)
    {
        headSt = selectedSt->nextSt;
    }
    
    if(tailSt == selectedSt)
    {
        tailSt = selectedSt->prevSt;
    }
    
    if(selectedSt->prevSt != NULL)
    {
        selectedSt->prevSt->nextSt = selectedSt->nextSt;
    }
    
    delete selectedSt;
    return *this;
}

Here i'm choosing to Delete (2 2 2 2) guy

Here destructor appears for some reason

【问题讨论】:

  • 首先,C++ 中不需要typedef。所有结构或类名都是类型名。
  • 请提供minimal reproducible example。现在我不知道你是如何运行程序的,你给程序提供了什么输入,你看到了什么输出。
  • 请更改您的析构函数以提供更多信息:cout &lt;&lt; "What!?: " &lt;&lt; this &lt;&lt; endl; -- 然后问问自己,该对象从何而来。您将看到它与您在调用函数中使用的StudentList 对象不同。
  • 非常感谢您的帮助
  • 此外,StudentList 对象中唯一可访问的函数是 StudentList()~StudentList()operator -。那么,如果其他所有内容都是 private,您如何编写一个对 StudentList 执行任何操作的程序?

标签: c++ class destructor doubly-linked-list


【解决方案1】:

你已经像这样声明了你的operator-

    StudentList operator-(student_nodePtr selectedSt);

请注意,它返回的是一个StudentList 对象按值。这意味着调用代码正在接收一个临时的StudentList 对象,然后当它超出范围时被销毁;因此调用 StudentList 析构函数。

通常的方式来声明这样的操作符是让它返回一个引用:

    StudentList & operator-(student_nodePtr selectedSt);

...这样不会创建临时的StudentList 对象,但如果需要,仍然可以将对运算符的调用链接在一起。

【讨论】:

    【解决方案2】:

    在将*this 作为StudentList 返回时,您正在创建一个临时 对象并返回它——这会导致一个析构函数调用。我认为这会工作,即使在这样的声明中:

    myList = myList - oneEntry - anotherEntry - aThird;
    

    这是因为每次减法创建的临时值用于下一次减法,而最终的临时值分配给myList

    但是,这些减法中的每一个都可能会导致 临时的,这将变得相当昂贵,尤其是对于较大的列表。

    最好返回一个引用StudentList&amp;,这样就不需要临时对象并返回原始对象。


    您的代码中的更多问题是,鉴于学生形成了一个 双重 链表,您可能应该在 operator- 中有一些东西可以调整反向指针和正向指针。

    您当前的实现正确处理空列表并删除头部/尾部,但随后继续只是这样做:

    if(selectedSt->prevSt != NULL) {
        selectedSt->prevSt->nextSt = selectedSt->nextSt;
    }
    

    这会调整前一个节点(如果有),使其现在指向下一个节点。但是,后续节点仍将指向即将被删除的节点作为其前一个节点。换句话说,这种情况会发生:

    +------+                       +------+
    | node |---------------------->| node |
    |      |                    +--|      |
    +------+  +--------------+  |  +------+
              | deleted node |<-+
              +--------------+
    

    您需要两个方向来保持一致的双向链表:

    if(selectedSt->prevSt != NULL) { // forward link (already done).
        selectedSt->prevSt->nextSt = selectedSt->nextSt;
    }
    if(selectedSt->nextSt != NULL) { // backward link (need to add).
        selectedSt->nextSt->prevSt = selectedSt->prevSt;
    }
    

    否则,如果您决定反向遍历,您的列表将会损坏并导致严重问题。

    【讨论】:

      猜你喜欢
      • 2011-03-23
      • 2014-05-02
      • 2012-05-10
      • 1970-01-01
      • 1970-01-01
      • 2019-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多