【问题标题】:Linked list destructor C++链表析构函数 C++
【发布时间】:2018-03-27 07:03:06
【问题描述】:

我正在学习使用链表实现 Stack。这是节点类:

class StudentInfo {
    public:
        string id, name, course;
        double GPA;
        StudentInfo *next;
};

这是 Stack 类:

class StackLinkedList {
    public:
        StudentInfo* top; //pointer to point to the top node
        int size; //variable to keep the size of the stack

    //constructor
    StackLinkedList() {
        this->size = 0;
        this->top = NULL;
    }

    //destructor
    ~StackLinkedList() {
        StudentInfo *current = top;
        while (top) {
            current = current->next;
            delete top;
            top = current;
        }
    }

    //to add item into stack - push on top
    void push(StudentInfo *newStudent) {
        if (!top) {
            top = newStudent;
            return;
        }

        newStudent->next = top;
        top = newStudent;
        size++;
    }

void main() {
    StudentInfo s1("phi", "123", "computer science", 4.0);
    StudentInfo s2("abc", "123", "software engineer", 4.0);
    StudentInfo s3("zxc", "123", "business management", 4.0);

    StackLinkedList list;
    StudentInfo *ptr;
    ptr = &s1;
    list.push(ptr);
    ptr = &s2;
    list.push(ptr);
    ptr = &s3;
    list.push(ptr);

};

当我尝试在 push() 和 printAll() 上运行单元测试时,一切正常。但是,在调用 destructor() 后,出现错误 Debug Assertion Failed … is_block_type_valid(header-> _block_use)。并且调试器在delete top;触发了断点

//destructor
~StackLinkedList() {
    StudentInfo *current = top;
    while (top) {
        current = current->next;
        delete top; //here
        top = current;
    }
}

如果我把top = NULL; 放在delete top; 之前,错误就消失了。所以,我对top = NULL; 声明有点困惑。 编辑:NodeType 的构造函数

 StudentInfo(string id, string name, string course, double gpa) {
        this->id = id; this->name = name; this->course = course; this->GPA = gpa; this->next = NULL;
}

【问题讨论】:

  • 首先,尝试运行 valgrind 并检查是否有任何内存泄漏。
  • 1) 不要使用原始的new/delete。使用智能指针(或只是对象的普通容器)。 2) 不要使用NULL。使用nullptr
  • @JesperJuhl 如果我尝试为指针返回类型返回 nullptr 是否有效?
  • @Phi Truong 你可以返回nullptr,当然。这是否是您想要以及您的呼叫者期望是另一个问题。

标签: c++ constructor linked-list aggregate destructor


【解决方案1】:

您通过尝试delete自动存储持续时间的对象调用了未定义行为。

int main() {
    StudentInfo s1("phi", "123", "computer science", 4.0);
    StudentInfo s2("abc", "123", "software engineer", 4.0);
    StudentInfo s3("zxc", "123", "business management", 4.0);

    StackLinkedList list;
    StudentInfo *ptr;
    ptr = &s1;
    list.push(ptr);
    ptr = &s2;
    list.push(ptr);
    ptr = &s3;
    list.push(ptr);

};

如您所见,s1s2s3自动存储期限的对象(也就是编译器在其生命周期结束时自动调用它们的析构函数)。

然而你将它们的地址传递给list,其析构函数deletes在其链表详细信息中的所有指针,在销毁时......永远不要在指向未创建对象的指针上调用delete使用new


一些补充说明:

  • void main() 在 C++ 中是非法的。您使用的是较旧的编译器吗? ..
  • 每个对象都应该管理它的资源。例如,std::forward_list 使用分配器在内部管理其节点的分配。我建议您重新设计StackLinkedList 以在内部管理其节点,这样客户端就不会为生命周期而烦恼。
  • 您应该阅读Rule of ThreeThe Rule of Five
  • 您的代码中还有一些其他错误,我没有触及。

【讨论】:

  • 还有一个问题,我不太明白top = NULL是怎么解决这个问题的?
  • 在你的top = NULL之后,下面的delete top变成了“删除NULL”,这基本上是一个无操作(不调用任何dtor,不释放任何内存)
  • @PhiTruong,如何在内部执行单元测试将超出这里的答案范围。但是,您将有很多 StackLinkedList 的测试用例作为 Unit。 ...至于您的第二条评论,Gian Paolo 已经解释过...
【解决方案2】:

对于初学者,您不会初始化 StudentInfo 类型的对象的数据成员 next

因此,所有依赖于列表中最后一个节点等于nullptr 的代码都将调用未定义的行为。

您也不能对不是用 operator new 创建的对象使用 operator delete。

所以而不是陈述

StudentInfo s1("phi", "123", "computer science", 4.0);
StudentInfo s2("abc", "123", "software engineer", 4.0);
StudentInfo s3("zxc", "123", "business management", 4.0);

你至少应该写(我假设StudentInfo 是一个聚合。如果该类有一个构造函数,那么将它声明为

StudentInfo( const string &id, 
             const string &name, 
             const string &course, 
             double gpa, 
             StudentInfo *next = nullptr ) 
{
        this->id = id; this->name = name; this->course = course; this->GPA = gpa; this->next = next;
}

) StudentInfo *s1 = new StudentInfo {"phi", "123", "computer science", 4.0, nullptr}; StudentInfo *s2 = new StudentInfo {"abc", "123", "软件工程师", 4.0, nullptr }; StudentInfo *s3 = new StudentInfo {"zxc", "123", "企业管理", 4.0, nullptr };

然后

list.push(s1);
list.push(s2);
list.push(s3);

【讨论】:

  • nullptr 应该使用什么参数类型?
  • @PhiTruong 你在说什么参数?它的类定义是一个聚合。
  • @PhiTruong 要么删除构造函数,因为它是多余的,因为所有数据成员都是公共的,或者使用与下一个数据成员对应的默认参数制作第五个参数。
  • 谢谢,我不知道,我可以在没有构造函数的情况下在 C++ 中启动一个对象。
猜你喜欢
  • 2015-08-03
  • 2019-04-09
  • 2013-04-26
  • 2020-10-07
  • 2021-07-20
  • 2013-03-18
  • 2012-10-15
  • 2016-01-31
相关资源
最近更新 更多