正确使用析构函数

时间:2017-03-20 17:48:01

标签: c++ dynamic-memory-allocation

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

我写了两个课程:

坐标:

#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类的对象。我编写了自己的Test析构函数,但我不确定这个析构函数是否像预期的那样工作。它是否完全取消了test的内存?或者我是否必须使用q属性取消分配它?

4 个答案:

答案 0 :(得分:9)

就你所做的而言,你所做的是正确的。 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 Five of Rule ,或者 - 甚至更好 - {{3 }} ,它表示你不应该执行任何newdelete,除非只存在于拥有存储空间的类中。

答案 1 :(得分:4)

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

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

在程序执行中,当调用delete test时,它将首先取消分配point的动态内存,然后使用test属性释放在main函数中设置的动态内存。通过这种方式你没有泄漏内存(耶!)你的内存管理已经相应地完成了:)

答案 2 :(得分:1)

您需要一个复制构造函数来确保内存管理正常。 因为隐式生成的构造函数和赋值运算符只是复制所有类数据成员(“浅拷贝”)。 而且,由于你的班级中有分配数据的指针,你真的需要它。

例如,如果您在主要代码中使用// ...,则执行以下操作:

Test testB = *test;

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

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

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

}

有了这个,你将确保每个Coordinate*初始化正常并且发布正常。

答案 3 :(得分:0)

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

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的规则。

在第二种情况下,您可能希望为您的类提供复制构造函数:

Test(const Test& rhs) : q(rhs.q), point(std::make_unique<Coordinate>(*rhs.point)) {}