【问题标题】:Delete Inherited class Pointer Array in C++在 C++ 中删除继承的类指针数组
【发布时间】:2020-01-07 10:27:22
【问题描述】:

我有一个像这样的 A 类:

class A
{
   int a;
}

而且,我还有继承了 A 类的 B 类:

class B : public A
{
   int b;

public:
   static A** ReturnAPtrArray(int size);
}

然后,我在 B 类中创建具有 A 类指针的 Array。

A** B::ReturnAPtrArray(int size)
{
   A** array = new A*[size];
   for(int i = 0; i< size; i++)
   {
      array[i] = new A();
   }

   return array;
}

在 main func 中,我调用了 class B 的 ReturnAPtrArray() func。

void main(void)
{
   int size = 100;
   A** aptrArray = B::ReturnAPtrArray(size);

   --------Do Something

   delete[] aptrArray;
}

这个主函数会导致内存泄漏。所以我删除了这样的每个指针:

void main(void)
{
    int size = 100;

    A** aptrArray = B::ReturnAPtrArray(size);

    --------Do Something

    for(int i = 0; i< size; i++)
    {
       delete aptrArray[i];
    }

    delete[] aptrArray;
}

修改main func后,内存泄漏消失了。

如果我想释放内存,我应该删除指针数组中的所有指针吗?

还有其他选择吗?

【问题讨论】:

  • 给A类添加一个虚析构函数,见stackoverflow.com/questions/461203/…
  • 每个new 一个delete,每个delete[] 一个new[]
  • std::vector&lt;std::unique_ptr&lt;A&gt;&gt; 避免手动内存管理。
  • 是的。 new 表达式的每次使用都必须与相应的 delete 表达式匹配。分配函数使用new[] 正好size + 1 次。要释放,必须在所有这些size + 1 分配上使用delete [](即,new [] 表达式给出的每个指针必须显式地提供给delete [] 表达式一次)。
  • 所以你使用指针是因为你可能有指向派生类的指针?那么class A 必须有虚拟析构函数,否则delete aptrArray[i]; 可能有未定义的行为。

标签: c++


【解决方案1】:

如果我想释放内存,我应该删除指针数组中的所有指针吗?

是的,你应该

delete[] 只删除它自己的数组。 由于您有一个指针数组,因此您必须单独删除每个指针元素。

至于其他选项,您可以使用智能指针。

例子:

#include <memory>
#include <vector>

int main()
{
    std::vector<std::shared_ptr<A>> array;
    for(int i = 0; i < 100; i++)
    {
        array.push_back(std::make_shared<B>());
    }
}

当数组超出范围时,它会自行删除它

【讨论】:

【解决方案2】:

区分多态所有权和多态使用很重要。多态所有权是指您想要拥有一个未知类型的事物(或许多事物)。多态使用是当你想在你不知道它是什么的时候操作它。您的示例并没有真正说明您为什么要使用继承,所以我将解释两者。

如果您只创建 B,只需将它们声明为 B。如果您想将一组 B 传递给一个不知道它们是 B 的函数,只需创建一个 B 的向量并将它们作为指向 A 的指针传递。
像这样……

std::vector<B> myBs(5);   // create 5 default B's
for (const auto& thisB: myBs) Fn(&thisB); // cast a ptr-to-B to a ptr-to-A

像这样保持简单会让你的生活变得更简单,因为这段代码是类型安全的。

另一方面,如果您想拥有一个可能是 B 的事物列表,也可能不是,但肯定是从 A 继承的。使用类似的东西。

std::vector<std::unique_ptr<A>> my_maybe_bs_might_not;

这种模式表面上看起来简单,但它有很多陷阱。例如,您必须使用必须使用虚拟析构函数。这反过来调用rule-of-3/5。简单地说,如果编译器不知道某个东西是什么,你必须告诉它如何移动/复制它。如果你不这样做,编译器可能会做错事。

一个更简单的(假设你有 C++17)多态所有权方案是使用variant。当你使用一个变体时,你必须列出它可能是的所有东西,这可以包括智能指针。

using MaybeB = std::variant<std::unique_ptr<B>, std::unique_ptr<C>>;
std::vector<MaybeB> my_maybe_bs_might_not;

此模式允许编译器为您生成所有正确的代码,同时使添加新类变得简单。唯一的缺点是因为你必须列出你可能想要拥有的所有东西。这对于库级系统来说是一个糟糕的选择,库的用户可能想要编写他们自己的类(从 A 派生)并将其添加到您的列表中。

一般建议是选择最简单的方案,这通常意味着避免多态所有权,除非确实需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-30
    • 2016-03-03
    • 1970-01-01
    • 2011-02-11
    • 2022-06-11
    • 2016-09-02
    • 1970-01-01
    相关资源
    最近更新 更多