我刚开始使用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
属性取消分配它?
答案 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 }} ,它表示你不应该执行任何new
或delete
,除非只存在于拥有存储空间的类中。
答案 1 :(得分:4)
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)) {}