【问题标题】:How should I deal with the destructor, when making a custom vector with a template class?在使用模板类制作自定义向量时,我应该如何处理析构函数?
【发布时间】:2022-06-10 21:06:55
【问题描述】:

我尝试使用模板类制作我的自定义 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);
}

所以我想,也许我应该使用复制构造函数,因为似乎问题是vvv 之前被删除。好吧,如果我只是注释掉析构函数代码,它会起作用,但我觉得这不太合适......

所以我做了一个这样的自定义复制构造函数:

Vector(const T& other) { 

}

但它给了我一个错误,说“模棱两可的重载”......回头看,当然是错误的,因为dataTotherT不同......

如何使我的自定义 Vector 类正常工作? (即我希望 push_back 按我的意图工作......)

【问题讨论】:

  • const T&amp; other -> const Vector&lt;T&gt;&amp; other?
  • 除非你初始化data,否则这个成员变量的值可以是任意的。除非此值恰好为 null,否则调用 delete[] 会导致未定义的行为,在这种情况下会使您的程序崩溃。 (您的默认构造函数不会将其初始化为 null。)顺便说一句:delete[] 可以使用 null 作为操作数来调用;在这种情况下,它根本什么都不做。在析构函数中检查 data 是否为 null 是不必要的。
  • 注意:如果你想让v[0].push_back(1);Vector&lt;Vector&lt;int&gt;&gt; 工作,你应该引用[] 运算符:T&amp; operator[](int i) { ... } 此外,我建议对索引使用无符号整数类型。通常size_t 用于此目的,这也是capacity/length 的首选类型。
  • 正确实现向量并非易事,因为它必须涉及放置new
  • 您的类构造函数显式地进行动态内存分配。您需要确保所有构造函数(包括复制/移动构造函数)在需要时分配内存,并且赋值(复制/移动)运算符在需要时正确重新分配内存,否则复制 Vector 的实例将导致析构函数释放一些内存两次 - 这会导致未定义的行为。这些函数的隐式生成的默认值不会这样做。有关更多信息,请查看“三规则”或(C++11 及更高版本)“五规则”。

标签: c++ vector containers destructor rule-of-five


【解决方案1】:

一般问题

在类设计中,尤其是涉及内存/资源分配时,通常需要遵循"Rule of Five"(在 C++11 之前曾是“三法则”):

如果您实现以下任何一项:

  • 析构函数
  • 复制/移动赋值运算符
  • 复制/移动构造函数

那么您可能需要实现所有这些。

原因是它们中的每一个都可能需要一些资源管理逻辑,超出了语言默认提供的内容。

对于您的班级,这五个方法的签名将是:

Method Signature
Copy constructor Vector(const Vector&amp;)
Move constructor Vector(Vector&amp;&amp;)
Copy assignment operator Vector&amp; operator=(const Vector&amp;)
Move assignment operator Vector&amp; operator=(Vector&amp;&amp;)
Destructor ~Vector() or virtual ~Vector()

您的特定班级

在您的具体情况下,有几个具体问题:

  • 正如@UnholySheep 建议的那样,您错误地声明了复制构造函数。
  • 您实现了默认构造函数;但是 - 它不分配任何东西,也不初始化任何东西! data 指针包含任意垃圾,当您尝试释放它时,很可能会发生坏事。
  • 您正在执行大量 T 值的复制,对于外部向量,这些值将是 Vector&lt;int&gt; 值 - 这可能会变得很昂贵。
  • 即使您修复了上述问题,您仍应实施“五法则”中缺少的方法。

【讨论】:

    【解决方案2】:

    您的默认构造函数使对象完全未初始化。

    考虑一下当你声明一个时会发生什么

    Vector<int> foo;
    

    foo 本质上得到一个随机内存地址为data,一个随机的lengthcapacity。如果你释放它,这将产生烟花。

    也许您通过始终创建具有预定义大小的向量来回避这个问题。幸运的是,尝试创建/销毁 Vector&lt;Vector&lt;int&gt;&gt; 会发现这一点,因为容器内的 Vector&lt;int&gt;[] 仍然包含这些定时炸弹。

    【讨论】:

      猜你喜欢
      • 2022-01-03
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多