【问题标题】:C++ shared pointers of parent-children classes父子类的C++共享指针
【发布时间】:2016-11-05 10:59:49
【问题描述】:

我有一个场景,我需要在一个集合中收集一个类型的所有对象,但我还需要一个它的一些继承类型的集合。示例:

class Particle: public someClass
{
    ...
    public:
        static std::vector<std::shared_ptr<Particle>> particleCollection;
}

class ChargedParticle: public Particle
{
    ...
    public:
    static std::vector<std::shared_ptr<ChargedParticle>> chargedParticleCollection;
}

但是,当我想销毁这些对象时,我实际上为每个 ChargedPartice 调用了两次析构函数:

Particle::particleCollection.clear(); // Okay
ChargedParticle::chargedParticleCollection.clear(); // Error: particles are already deleted

我怎样才能将子对象的集合存储在其静态容器中,并让其父类之一指向它们的智能指针?

我也希望能够从父类创建对象,并让父类的静态智能指针向量成为这些对象的所有者。

我的想法是,当对象不是子类集合的元素时,我以某种方式为仅调用析构函数的父类智能指针定义了一个自定义删除器。这可能吗?

【问题讨论】:

  • 为什么要用静态成员来存储这个粒子?
  • 正确使用shared_ptr 时,不应发生双重析构函数调用。您应该显示更多代码,可能是 mcve:stackoverflow.com/help/mcve
  • @АлександрЛысенко 简单的用例:我想计算带电粒子上的力,由于静电相互作用。为此,我需要知道所有其他带电粒子的位置。
  • 也许你应该只在基类中保留粒子?不创建可能的重复项?
  • 从 cppreference,只允许在以前共享的对象上调用 shared_from_this,即在由 std::shared_ptr 管理的对象上。否则行为未定义(C++17 前)std::bad_weak_ptr 被抛出(由默认构造的weak_this 中的shared_ptr 构造函数)(C++17 起)。

标签: c++ destructor smart-pointers ownership-semantics


【解决方案1】:

每个ChargedParticle 同时是一个Particle,因此调用Particle::particleCollection.clear(); 就足以删除所有分配的对象。

要在您的情况下使用共享指针,您需要从std:: enable_shared_from_this 继承基类(someClass 或Particle),因此从它创建的共享指针将共享相同的计数器。在您的示例中,这是两个不同的共享指针实例,彼此一无所知。

而且我认为虚拟析构函数不足以满足您的需求。

【讨论】:

  • 对particleCollection 的清除对我处理chargedParticleCollection 没有帮助。如果我在它上面循环单向清除后,它将包含悬空指针。我使用虚拟析构函数,但我从未听说过这个 std::enable_shared_from_this 类。我去看看,谢谢:)
  • @AdamHunyadi 您可以考虑使用std::weak_ptr,或者在析构函数中从适当的数组中删除项目。
  • 在这种情况下如何使用 std::weak_ptr?
  • @AdamHunyadi 弱指针有成员 expired 可以判断对象是否已被删除。但这一切都取决于您希望如何管理所有权
  • 我明白了,但是如何将派生类的弱指针指向向量?我不能让它们成为基类的弱点,因为我在派生对象的集合中使用它们。我也不能让它成为派生的弱 ptr,因为那样我就不能让它坚持基类的共享 ptr。
【解决方案2】:

如果你使用得当,没有问题,类名和你的类名不一样,但我觉得没关系:

#include <iostream>
#include <type_traits>
#include <tuple>
#include <vector>
#include <memory>
struct Base
{
    Base() { std::cout << "  Base::Base()\n"; }
    // Note: non-virtual destructor is OK here
    ~Base() { std::cout << "  Base::~Base()\n"; }
};

struct Derived: public Base
{
    Derived() { std::cout << "  Derived::Derived()\n"; }
    ~Derived() { std::cout << "  Derived::~Derived()\n"; }
};

int main() {  
    std::vector<std::shared_ptr<Base>> base_vector;
    std::vector<std::shared_ptr<Derived>> derived_vector;
    auto d = std::make_shared<Derived>();
    derived_vector.push_back(d);
    base_vector.push_back(d);
    // 2 function call below does not matter
    base_vector.clear();
    derived_vector.clear();

}

Demo 扩展自 Cpp Reference 中的示例

你不能用std::shared_ptr创建一个自注册类,因为你需要从std::enable_shared_from_this继承,但是你不能在构造函数中调用shared_from_this(),因为:

只允许在先前共享的对象上调用 shared_from_this,即在 std::shared_ptr 管理的对象上。否则行为未定义(C++17 前)std::bad_weak_ptr 被抛出(由默认构造的weak_this 中的shared_ptr 构造函数)(C++17 起)。

【讨论】:

  • 这不好,因为在我的基类构造函数调用中,我不能有一个指向派生类的指针。
  • @AdamHunyadi 这意味着您想在该列表中自行注册您的课程?
  • @AdamHunyadi 问题是,当构造函数没有完成时,对象不存在,如果构造函数抛出,对象不存在,这说明你的向量有一个非存在对象
  • 因此,请考虑改用工厂设计模式
  • @AdamHunyadi 更清楚地说,std::enable_shared_from_this 抱怨 bad weak_ptr,正如你所看到的 here 和 here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
相关资源
最近更新 更多