正确使用析构函数

Right usage of destructor

我刚开始使用 C++,现在我有一个非常基本的问题。

我写了 2 classes:

坐标:

#include <stdio.h>

class Coordinate {
private:
    int x;
    int y;
public:
    Coordinate(int a, int b) {
        x = a;
        y = b;
    };
    void printTest() {
        printf("%d %d\n", x, y);
    };
};

测试:

class Test {
private:
    int q;
    Coordinate *point;
public:
    Test(int a, int b, int c) {
        q = a;
        point = new Coordinate(b, c);
    };
    virtual ~Test() {
        delete point;
    }
};

主要功能:

int main() {
    Test *test = new Test(1, 2, 3);
    // ...
    delete test;
    return 0;
}

在我的 main 中,我使用了 Test class 的对象。我写了自己的 Test 析构函数,但我不确定这个析构函数是否按预期工作。它是否完全释放了 test 的内存?还是我必须对 q 属性做些什么才能释放它?

是的,这是 C++ 析构函数的正确用法(您在 Test 中的析构函数不需要 virtual 关键字,因为您的示例中没有继承)。

根据经验,每个 new 后面都应该跟一个 delete,但是当您使用 class 和实例化时,它会变得更加微妙。在 Rule of Three or Five 之后,当 class 使用动态内存时,您应该重新定义 class 析构函数 以相应地释放,您所做的,太棒了!

在您的程序执行中,当调用 delete test 时,它将首先释放 point 的动态内存,然后再释放您在 main 函数中使用测试属性设置的动态内存。通过这种方式,您不会泄漏内存(耶!)并且您的内存管理已相应完成:)

就目前而言,您所做的是正确的。 Valgrind 报告

==28151== HEAP SUMMARY:
==28151==     in use at exit: 0 bytes in 0 blocks
==28151==   total heap usage: 3 allocs, 3 frees, 72,736 bytes allocated
==28151== 
==28151== All heap blocks were freed -- no leaks are possible

您缺少的是编译器为您提供了默认的复制构造函数和赋值运算符。这些将复制指针,而不是创建一个新的指向值,所以任何时候你复制一个Test对象你会然后有两个对象,其析构函数 都将尝试删除相同的存储 。这是一个双重免费,它会毁了你的一天。

为了避免这种情况,C++ 程序员使用 Rule of Three or Rule of Five when writing classes, or - even better - the Rule of Zero,它说你不应该做任何 newdelete 除了class 只为拥有存储空间而存在。

您需要一个复制构造函数来确保内存管理正常。 因为隐式生成的构造函数和赋值运算符只是简单地复制所有 class 数据成员 ("shallow copy")。 由于您的 class 中有分配数据的指针,因此您确实需要它。

例如,如果在你的主代码部分:// ... 你做了一个副本:

Test testB = *test;

testB 有一个 Coordinate 指针指向与 *test 相同的内存区域。这可能会导致问题,例如,当 testB 超出范围时,它将释放 *test 正在使用的相同内存。

复制构造函数应该如下所示:

Test(const Test& other)
    : point (new Coordinate(other.x, other.y))
    , q(other.q)
{

}

有了这个,您将确保每个 Coordinate* 都可以正常初始化并正常发布。

在你的情况下,你不需要指针

class Test {
private:
    int q;
    Coordinate point;
public:
    Test(int a, int b, int c) : q(a), point(b, c) {};
};

int main() {
    Test test(1, 2, 3);
    // ...
    return 0;
}

足够了。

如果要分配内存,强烈建议改用智能指针或容器:

class Test {
private:
    int q;
    std::unique_ptr<Coordinate> point;
public:
    Test(int a, int b, int c) : q(a), point(std::make_unique_ptr<Coordinate>(b, c)) {};
};

int main() {
    auto test = std::make_unique<Test>(1, 2, 3);
    // ...
    return 0;
}

无论哪种方式,您都遵守 3/5/0 规则。

在第二种情况下,您可能应该想为您的 class:

提供复制构造函数
Test(const Test& rhs) : q(rhs.q), point(std::make_unique<Coordinate>(*rhs.point)) {}