【问题标题】:C++ Returning exception, Should I free resources?C++ 返回异常,我应该释放资源吗?
【发布时间】:2020-06-13 13:22:53
【问题描述】:

我为我的构造函数编写了以下实现:

template<class T>
Matrix<T>::Matrix(const Dimensions &matrix_dimensions, const T &initial_value) : dimensions(matrix_dimensions),
                                                                                 data(new T[size()]) {
    if (matrix_dimensions.getRow() <= 0 || matrix_dimensions.getCol() <= 0) {
        return IllegalInitialization();
    }
    int matrix_size = size();
    for (int i = 0; i < matrix_size; ++i) {
        data[i] = initial_value;
    }
}

因此,如果发生错误,我将返回 IllegalInitialization() o 被缓存,但是我使用 new 分配的资源呢?在返回之前如何释放它们,我应该调用删除吗?

只是一个旁注:我是这个领域的新手,所以有人可以检查上面的代码是否写得很好(性能方面并且在理论上按预期工作)

【问题讨论】:

  • 这方面有什么帮助吗?
  • 你为什么允许Dimensions 保留负值?既然行数和列数不会是负数,为什么不用unsigned 类型来表示呢?无论哪种方式,如果给定非正维度,为什么不让 Dimensions 类型抛出异常?这样,Matrix 可以安全地假设尺寸是有效的(因为在 Matrix 甚至构造之前会引发异常。
  • 至于delete []对应的new []是否被执行——这取决于。如果data 是一个带有析构函数的类型,它在它接收到的指针上执行相应的delete [],那么所有的都将被清除。但是,如果data 是其他任何东西(例如原始指针),则不会释放内存。推测Matrix 的析构函数会进行这样的清理,但是在Matrix 的构造函数主体中抛出异常时,Matrix 的析构函数不会被调用。
  • 如何从构造函数返回值?你的意思是throw 吗?
  • @Peter 数据可以是字符串、整数、双精度或自定义类

标签: c++ class generics exception methods


【解决方案1】:

假设您的意思是 throw 而不是 return(您不能从构造函数返回值!)因此对象的创建被取消,是的,您必须首先 delete[] data

通常的方法是递归地应用“资源获取即初始化”模式。也就是说,data 的类型应该是一个原始指针,而是一些智能类型(如std::unique_ptr&lt;T[]&gt;),当它超出范围时会自动执行删除。

std::unique_ptr 在内部是异常安全的。)

如果您确实只是早熟-returning,那么您将遇到更大的问题 — 对象处于无用状态而拥有范围不知道它,并且您使用 return 参数构建失败(因为,同样,您不能从构造函数返回值!)。

【讨论】:

  • 试一试,告诉我们你的进展情况。
【解决方案2】:

首先,问题中的代码永远无法编译,因为构造函数无法返回值。因此,构造函数中的语句return IllegalInitialization(); 是一个可诊断的错误。

出于此答案的目的,我假设您实际上使用throw IllegalInitialization();

其次,显示的代码可能存在泄漏。 datanew 表达式初始化,因此data 的类型必须是指向与T 兼容的类型的指针(例如,指向T 基的指针)或者它必须是一个类使用接受此类指针的构造函数键入(例如std::unique_ptr&lt;T&gt;)。如果data 是任何其他类型(例如intdouble),则data 的初始化是一个可诊断的错误,因为(例如)指针不能隐式转换为int

如果data 是原始指针,那么在构造函数中抛出异常将导致泄漏。使用new 表达式分配的对象或数据不会被释放。

如果data 是一个具有接受指针的构造函数的类,那么有两种可能性(其中代码将编译,并且没有未定义的行为)。

  1. 类构造函数保存指针,析构函数释放它(使用delete [],因为它对应于new表达式),然后动态分配的数据将被安全释放。这种类的一个例子是类型std::unique_ptr&lt;T[]&gt;。这是唯一您的代码可以避免泄漏的情况。
  2. 否则会出现内存泄漏。即使Matrix&lt;T&gt; 的析构函数清理了data,在构造函数主体中抛出异常的行为也会阻止Matrix&lt;T&gt; 的析构函数被调用(只能为已完全构造的对象调用析构函数 - 并且这意味着构造函数已经完成,没有抛出异常)。

(还有其他情况,例如 data 是具有使用错误形式的 delete 表达式的析构函数的类的实例,但这些情况要么给出未定义的行为,要么以其他方式泄漏,所以我'会忽略它们)。

第三(这更主观),您使用Dimensions 类型(可能包含一对整数值)来表示矩阵维度。从本质上讲,您在Matrix 中有一个要求,即尺寸为正数。由于如果尺寸为非正数,您将引发异常,我建议由Dimensions 类完成检查(例如,如果给定非正值,构造函数将抛出,setter(如SetRow() 要么忽略无效值, 或在给定无效值时抛出异常). 如果 Dimensions 类型始终如一地做到这一点,则 Matrix&lt;T&gt; 可以安全地假设尺寸是有效的 - 因为不可能传递 Dimensions 的实例具有无效值。这意味着,Matrix&lt;T&gt; 不需要检查。

【讨论】:

  • 谢谢,你能告诉我我编辑的代码应该是什么样子吗,我正在做一个大项目,获得一个样本真的很有帮助。谢谢你的解释!
  • 我已经描述了您的代码需要做什么才能确保安全(或者至少更安全)——更新代码是您的工作。这不是一项免费的编码服务——与人们简单地用勺子喂你代码相比,你将通过自己研究如何去做来学到更多。此外,请考虑使用标准容器(std::vector 等) - 虽然可能以不安全的方式使用它们,但如果您阅读文档并遵循指导,默认情况下它们往往比手动滚动您自己的代码更安全做内存分配。
猜你喜欢
  • 1970-01-01
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
  • 2010-09-14
  • 1970-01-01
  • 2018-12-31
  • 2010-09-08
相关资源
最近更新 更多