【问题标题】:Deleting Pointer shared with other class删除与其他类共享的指针
【发布时间】:2017-09-04 11:30:00
【问题描述】:

我在 C++ 中使用类之间共享的指针释放内存时遇到问题。

一个例子:

我的顶点定义为:

class Vertex{
    double x;
    double y;
}

正方形定义为:

class Square{
    Square(Vertex* a, Vertex* b, Vertex* c, Vertex* d);
    ~Square(); // destructor
    Vertex* a;
    Vertex* b;
    Vertex* c;
    Vertex* d;
}

我的析构函数是这样实现的:

Square::~Square(){
    delete a;
    delete b;
    delete c; 
    delete d;
}

我的方块存储在std::vector<Square*> squares,所以为了清理我所有的记忆,我会这样做:

for(unsigned int i = 0; i < squares.size(); i++){
    delete(squares.at(i));
}

那么问题是什么?如果两个正方形共享一个顶点,我的程序会崩溃,因为它试图删除一个不再存在的指针。 我怎么解决这个问题?

【问题讨论】:

  • 不要手动分配内存?
  • 使用std::shared_ptr。这样,您就不必自己管理内存。
  • 指向顶点的共享指针拼写为std::shared_ptr&lt;Vertex&gt;
  • Using std::shared_ptr 如果没有更多对象使用它,它将调用析构函数。或者如果相同的顶点是“共享的”,则使用堆栈上的数据会导致冗余。对于像你这样的小对象:Vertex 这并没有太多的内存开销。
  • @AndreKampling 使用std::vector 代替std::shared_ptr 有很多区别?

标签: c++ pointers memory-management shared-ptr


【解决方案1】:

在我看来,您是在以类似 Java 的思维方式使用 C++ 进行编码。 Vertex 仅包含两个 doubles 的对象(例如,在您的情况下,X 和 Y 组件)更好地存储在堆栈中,而无需指针间接。所以,我会像这样声明Square 类:

class Square{
...
  Vertex a;
  Vertex b;
  Vertex c;
  Vertex d;
};

如果您想要一种引用机制而不是嵌入Vertex 对象,则可以将顶点存储在std::vector&lt;Vertex&gt; 数组中,并存储在Square 类整数中索引 到数组中的顶点位置。

如果您真的想要与指针共享所有权语义,那么请考虑使用像std::shared_ptr 这样的智能指针。没有明确的deleteshared_ptr 将在引用计数达到零时自动释放内存。

在这种情况下,将 Square 类中的原始 Vertex* 拥有指针数据成员替换为 shared_ptr&lt;Vertex&gt;。此外,从Square 类中删除析构函数代码,因为shared_ptr 知道如何删除自己。

在您的Square 类构造函数中,您可以按值获取shared_ptr&lt;Vertex&gt; 智能指针,并将std::move 它们放入相应的数据成员中,例如:

Square::Square(
  std::shared_ptr<Vertex> pa,
  std::shared_ptr<Vertex> pb,
  std::shared_ptr<Vertex> pc
)
  : a{std::move(pa)}
  , b{std::move(pb)}
  , c{std::move(pc)}
{}

也将vector&lt;Square*&gt; 替换为vector&lt;shared_ptr&lt;Square&gt;&gt;(但是,您确定更简单的vector&lt;Square&gt; 不能很好地为您服务吗?),并使用std::make_shared 创建智能指针。

【讨论】:

  • @ChrisDrew:在这种情况下,那些将是观察指针,而不是拥有指针。
  • 我知道我提供了一个非常简单的示例,但我确实需要在不同的类之间共享指针,因此我无法将顶点保存在堆栈上。如果我使用shared_ptr,我将不得不更改很多代码而不是std::vector
  • @Steve 如果您真的想要shared_ptr,那么您可以在Square 类中定义shared_ptr&lt;Vertex&gt; 数据成员(而不是原始Vertex*)。此外,从Square 类中删除析构函数代码,因为shared_ptr 知道如何删除自己。也将vector&lt;Square*&gt; 替换为vector&lt;shared_ptr&lt;Square&gt;&gt;,并使用std::make_shared 创建智能指针。
  • 谢谢,现在我试试。如果您编辑答案,我可能会投票作为解决问题的答案
  • @Steve 我已经编辑了我的答案,并结合了shared_ptr 用法的评论说明。
【解决方案2】:

在现代 C++ 中,您永远不应该手动分配内存,除非您有充分的理由这样做。如果您的 Square 类想要模拟Vertex 实例的共享所有权,那么您应该使用std::shared_ptr

class Square{
    // ...
    std::shared_ptr<Vertex> a, b, c, d;
}

不过,我建议您重新考虑将动态内存分配用于像 Vertex 这样简单和轻量级的东西。

【讨论】:

  • 我知道我提供了一个非常简单的示例,但我确实需要在不同的类之间共享指针,因此我无法将顶点保存在堆栈上。我的std::vector&lt;Square*&gt; 不在 Square 课上,所以我认为在那个课上使用 share_ptr 不好......或者它只是一个例子?
【解决方案3】:

如果您使用的是旧代码并且无法将原始指针更改为共享指针,或者您不能简单地复制顶点(您想引用原始对象),则不要删除 Square 析构函数中的顶点,并且可能使用对顶点的引用而不是指针。

您已经在 Square 对象之外管理顶点的分配,因此请确保只有在所有正方形都消失后才解除分配顶点。

代码流程如下所示:

  • 分配所有顶点:容器跟踪所有顶点
  • 在多个对象中使用顶点,可能通过对它们的引用
  • 当所有使用顶点的对象都消失时,释放容器中的所有顶点(如果容器存储的是顶点对象而不是指针,则只需清除容器)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 2018-04-03
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    相关资源
    最近更新 更多