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



我想在程序结束时删除所有节点来释放内存,但我也有删除特定节点的函数(重载运算符(。如果我正在删除特定的节点类,则调用析构函数。有人能解释为什么,以及如何修复它吗?

类别声明

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

这里我选择删除(2 2 2 2(家伙

由于某种原因,这里出现了析构函数

您已经这样声明了您的operator-

StudentList operator-(student_nodePtr selectedSt);

请注意,它通过值返回一个StudentList对象。这意味着调用代码正在接收一个临时StudentList对象,当它超出范围时,该对象就会被销毁;从而调用CCD_ 4析构函数。

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

StudentList & operator-(student_nodePtr selectedSt);

这样就不会创建临时StudentList对象,但如果需要,对操作员的调用仍然可以链接在一起。

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

myList = myList - oneEntry - anotherEntry - aThird;

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

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

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


您的代码中更大的问题是,给定学生形成链表,您可能在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;
}

如果没有这一点,如果你决定反向遍历,你的列表就会被破坏并引发严重问题。

最新更新