C++ 中的类析构函数

Class destructor in C++

提问人:comediann 提问时间:8/27/2023 最后编辑:Peter Mortensencomediann 更新时间:9/4/2023 访问量:129

问:

我创建了一个具有一些类属性的类书,例如名称和作者(没有动态分配),然后我创建了一个包含动态书数组(如 book* set)及其大小和一些方法的类库,因此在库类中,我向析构函数添加了一个“delete”运算符来释放“set”的内存,因为 set 是一个指针, 但是在书课上,我只添加了一些文本,例如“析构函数被调用”,以了解程序执行后我所有的书是否会被删除。

主要问题是,首先我创建了三个 book 对象,然后我创建了一个 library 对象,在那里我添加了所有 3 本书。当我运行我的代码时,我看到库对象析构函数的消息,除了 3 之外只有一个书籍析构函数,为什么会发生这种情况?是否调用非动态对象的析构函数真的重要吗?

因为我知道,如果不调用动态对象的析构函数,这是一个真正的问题,因为它会导致内存泄漏,但是我的情况呢?

它如何实际适用于局部对象和一般动态对象?

class book
{
private:
    string title;
    string author;
public:
    book() { title = ""; author = ""; }
    book(string n, string a) : title(n), author(a) {}
    string getTitle() { return title; }
    string getAuthor() { return author; }

    book& operator=(const book& other)
    {
        title = other.title;
        author = other.author;
        return *this;
    }

    ~book() { cout << "Destructor of book \"" << title << "\" " <<  "was called!\n"; }
};

class library
{
private:
    book* set;
    int size;
    int ptr;
public:
    library()
    {
        size = 2;
        ptr = 0;
        set = new book[size];
    }

    void append(book &a)
    {
        if(ptr < size)
        {
            set[ptr] = a;
            ptr++;
        }
        else // Here I am just treating it as a simple dynamic
             // array, so if I run out of space, I create a
             // new dynamic array with n*2 size
        {
            book* copy = new book[size*2];
            for(int i = 0; i < size; i++)
                copy[i] = set[i];
            set = copy;
            set[ptr++] = a;
            size *= 2;
        }
    }

    ~library()
    {
        cout << "destructor of library was called.\n";
        delete set;
    }

};

int main()
{
    book one("Harry Potter", "J. K. Rowling");
    book two("The Little Prince", "Antoine de Saint-Exupery");
    book three("The Secret garden", "Frances Hodgson Bumett");
    library myLib;
    myLib.append(one);
    myLib.append(two);
    myLib.append(three);
    return 0;
}
C++ OOP 指针 析构函数

评论

3赞 Quimby 8/27/2023
请展示代码而不是描述它,最好以最小的可重现示例的形式。
0赞 yeputons 8/27/2023
没有代码就无法回答,请提供 MRE。此外,不要使用 , use(或者如果出于某种原因,例如由于继承而确实必须使用),也不要将 delete[]delete 混淆set<Book*>set<Book>set<unique_ptr<Book>>
2赞 Jeremy Friesner 8/27/2023
如果可以选择是否使用指针,则应避免使用指针。在大多数情况下,它们不是必需的,并且很难正确使用它们。
2赞 HolyBlackCat 8/27/2023
尽管有些教程使它看起来,但 、 和析构函数(以及自定义复制构造函数和赋值运算符)是大多数程序员几乎不应该使用的高级功能。首选 、 等。newdeletevectorunique_ptr
1赞 molbdnilo 8/27/2023
你写了你应该写的地方,触发了未定义的行为。deletedelete []

答:

2赞 tbxfreeware 8/28/2023 #1
你的班级是伪装的。librarystd::vector

如果将成员函数的名称更改为 ,则可以真正看到相似性。appendpush_back

第1项质询

当我运行我的代码时,我看到库对象析构函数的消息,除了 3 之外只有一个书籍析构函数,为什么会发生这种情况?

这里的问题在评论中被多次发现。您忘记使用运算符的数组版本。以下是您在问题中的代码:delete

    ~library()
    {
        cout << "Destructor of library was called.\n";
        delete set;
    }

这是更正:

    ~library()
    {
        cout << "Destructor of library was called.\n";
        delete[] set;
    }

通过这个简单的更改,一切都会立即到位。

输出如下:

Destructor of library was called.
Destructor of book "" was called!
Destructor of book "The Secret garden" was called!
Destructor of book "The Little Prince" was called!
Destructor of book "Harry Potter" was called!
Destructor of book "The Secret garden" was called!
Destructor of book "The Little Prince" was called!
Destructor of book "Harry Potter" was called!

起初,调用析构函数似乎很奇怪。然而,这可以通过以下事实来解释:默认构造函数 是由 operator 调用的。默认构造函数为 调用了两次,当您从函数 调用运算符时,它被调用了四次。book ""booknewlibrarynewappend

在附加了三本书后,图书馆的最后一个插槽仍未使用。这就是您在输出中看到的被破坏的那个。

数组的元素以其构造的相反顺序被销毁。这解释了上面输出中显示的析构函数调用的顺序。前四个节目被摧毁了。最后三个显示了函数中被销毁的局部变量。setmyLibmain

哦不!内存泄漏...

在函数中,您忘记删除旧数组。这是修复:append

    void append(book& a)
    {
        if (ptr < size)
        {
            set[ptr] = a;
            ptr++;
        }
        else   // Here I am just treating it as a simple dynamic array, so if I run out of space, I create a new dynamic array with n*2 size
        {
            book* copy = new book[size * 2];
            for (int i = 0; i < size; i++)
                copy[i] = set[i];
            std::swap(set, copy);  // Swap the pointers
            delete[] copy;         // Then, delete the copy.
            set[ptr++] = a;
            size *= 2;
        }
    }

这为您提供了对析构函数的另外两个调用:book

Destructor of book "The Little Prince" was called!
Destructor of book "Harry Potter" was called!
Destructor of library was called.
Destructor of book "" was called!
Destructor of book "The Secret garden" was called!
Destructor of book "The Little Prince" was called!
Destructor of book "Harry Potter" was called!
Destructor of book "The Secret garden" was called!
Destructor of book "The Little Prince" was called!
Destructor of book "Harry Potter" was called!
第2项质询

是否调用非动态对象的析构函数真的重要吗?

这个问题没有实际意义。每当对象的生存期结束时,都会调用对象的析构函数。即使在堆栈上分配了对象,调用析构函数也很重要。你的班级就是一个例子。虽然它不包含您创建的任何动态分配的数据,但它确实包含一对对象,并且这些对象可能确实将数据存储在堆上。bookstd::string

生产代码呢?

您的库程序可能是了解动态分配数据的好练习。每个人迟早都需要做这样的事情。但是,在生产代码中,最好使用标准库中的容器之一。

class library
{
private:
    std::vector<book> books;
public:
    library() {
        books.reserve(2);
    }
    void append(book& a) {
        books.push_back(a);
    }
    // ...
};

您甚至可以完全消除类:library

int main()
{
    book one("Harry Potter", "J. K. Rowling");
    book two("The Little Prince", "Antoine de Saint-Exupery");
    book three("The Secret garden", "Frances Hodgson Bumett");
    std::vector<book> myLib;
    myLib.push_back(one);
    myLib.push_back(two);
    myLib.push_back(three);
    return 0;
}

评论

0赞 comediann 8/28/2023
你回答了我所有的问题,非常感谢你,你帮了我很多!