提问人:Hashnut 提问时间:6/10/2022 最后编辑:Peter MortensenHashnut 更新时间:6/10/2022 访问量:89
当使用模板类制作自定义向量时,我应该如何处理析构函数?
How should I deal with the destructor, when making a custom vector with a template class?
问:
我尝试使用模板类制作自定义 Vector 类。
我希望我可以把我的放到一个变量中。至少这是我所希望的......但它在析构函数代码处不断崩溃。Vector<int>
Vector<Vector<int>>
这是我的代码。
#include <iostream>
#include <string>
template <typename T>
class Vector {
T* data;
int capacity;
int length;
public:
typedef T value_type;
Vector() {}
Vector(int n) : data(new T[n]), capacity(n), length(0) {}
void push_back(T input) {
data[length++] = input;
}
T operator[](int i) { return data[i]; }
virtual ~Vector() { if (data) delete[] data; }
};
int main() {
Vector<Vector<int>> v(3);
Vector<int> vv(4);
v.push_back(vv);
}
所以我想,也许我应该使用复制构造函数,因为似乎问题是之前被删除了。好吧,如果我只是注释掉析构函数代码,它会起作用,但这对我来说似乎不对......v
vv
所以我做了一个自定义的复制构造函数,如下所示:
Vector(const T& other) {
}
但它给了我一个错误,说“模棱两可的超载”......回头看,这当然是错误的,因为 of 与 of 不同......T
data
T
other
如何使我的自定义类正常工作?(即我想push_back按照我的预期工作......Vector
答:
5赞
Botje
6/10/2022
#1
默认构造函数使对象完全未初始化。
考虑一下当您声明
Vector<int> foo;
foo
本质上得到一个随机的内存地址,如 、 和 。如果你释放它,这将产生烟花。data
length
capacity
也许您通过始终使用预定义大小创建矢量来回避这个问题。幸运的是,试图创建/摧毁一个会暴露这一点,因为你的容器内部仍然包含这些滴答作响的定时炸弹。Vector<Vector<int>>
Vector<int>[]
6赞
einpoklum
6/10/2022
#2
一般问题
在类设计中,特别是当涉及内存/资源分配时,通常需要遵循“五法则”(在 C++ 11 之前曾经是“三法则”):
如果实现以下任何一项:
- 析构函数
- 复制/移动赋值运算符
- 复制/移动构造函数
那么你可能需要实现所有这些。
原因是它们中的每一个都可能需要一些资源管理逻辑,而不是语言给出的默认值。
对于您的班级,这五种方法的签名将是:
方法 | 签名 |
---|---|
Copy 构造函数 | Vector(const Vector&) |
Move 构造函数 | Vector(Vector&&) |
复制赋值运算符 | Vector& operator=(const Vector&) |
移动赋值运算符 | Vector& operator=(Vector&&) |
破坏者 | ~Vector() 或virtual ~Vector() |
您的特定班级
在您的具体案例中,存在几个具体问题:
- 如@UnholySheep所示,您错误地声明了复制构造函数。
- 您实现了一个默认构造函数;但是 - 它不分配任何东西,也不初始化任何东西!指针包含任意垃圾,当您尝试释放它时,可能会发生不好的事情。
data
- 您正在执行相当多的值复制,对于外部向量来说,这些值将是值 - 这可能会变得昂贵。
T
Vector<int>
- 即使您修复了上述问题,您仍然应该实现“五法则”中缺少的方法。
评论
const T& other
->const Vector<T>& other
?data
delete[]
delete[]
data
v[0].push_back(1);
Vector<Vector<int>>
[]
T& operator[](int i) { ... }
size_t
capacity
length
new
Vector