如果我删除链表元素,为什么调用 class 析构函数?

Why class destructor called if I delete linked list element?

我想通过删除程序末尾的所有节点来释放内存,但我也有删除特定节点的函数(重载运算符)。如果我要删除特定节点 class,则调用析构函数。谁能解释一下原因,以及如何解决它。

Class声明

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

*this 作为 StudentList 返回时,您正在创建一个 临时 对象并返回它 - 这会导致析构函数调用。我认为这将 起作用, 即使在这样的语句中也是如此:

myList = myList - oneEntry - anotherEntry - aThird;

这是因为在每次减法时创建的临时文件用于下一次减法,最后的临时文件被分配给 myList

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

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


你的代码中更有问题的是,假设学生形成了一个 链表,你可能应该在 operator- 中有一些东西来调整反向指针以及前锋。

您当前的实现正确地处理了空列表并删除了 head/tail,但随后 只是 这个:

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;
}

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

您已这样声明您的 operator-

    StudentList operator-(student_nodePtr selectedSt);

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

声明这样的运算符的通常方法是将其 return 改为引用:

    StudentList & operator-(student_nodePtr selectedSt);

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