防止对被删除对象的函数调用

时间:2017-12-11 16:46:58

标签: c++ multithreading smart-pointers undefined-behavior dangling-pointer

我试图解决由于对被破坏的对象调用方法而导致应用程序崩溃的问题。我有以下课程:

// Forward declarations
class A;

class B
{
public:
    B(A* aPtr) : m_pA(aPtr) { Schedule(); }
    ~B() { Timer::ref().Cancel(m_TimerId); }

    void Schedule();
    void Update();

private:
    A* m_pA;
    int32_t m_TimerId;
}

void B::Schedule()
{
    m_TimerId = Timer::ref().Schedule(1000, [this]() { Update(); });
}

void B::Update()
{
    m_pA->DoSomething();
}

class A
{
public:
    void Create() { m_B = std::make_unique<B>(); }
    void DoSomething() {}

private:
    std::unique_ptr<B> m_B;
}

基本上A类将unique_ptr保存到B类,它将原始ptr存储到A.B使用Timer的单例实例,在不同的线程中运行,并且它调度函数B :: Update()每1000ms在该线程上执行一次。有时当我销毁将A作为成员变量存储在堆栈上的对象时,A被销毁,但是在类B内有一个悬挂指针,导致函数{{1}从另一个线程调用,以使应用程序崩溃,因为m_pA *不是B::Update(),因此这会导致未定义的行为。

我该如何处理,以防止我的应用程序崩溃?明确设置nullptr;在m_pA = nullptr足够吗?

2 个答案:

答案 0 :(得分:1)

  

明确设置m_pA = nullptr;在B ::〜B()中足够吗?

没有。这会让你头疼,因为你不仅会有竞争条件(考虑线程读取值的情况,指针设置为null,对象被删除,然后线程尝试使用该对象) ,您最终还会尝试从刚刚删除的对象中读取内存。

  

如何处理此问题,以防止我的应用程序崩溃?

确保B的生命周期比线程长(最简单的方法是使B拥有该线程),并确保在访问时(如果是读/写)保护所有共享内存。请记住,您不仅要确保在已删除的数据上调用update();但是在调用更新时你不能删除数据。

答案 1 :(得分:0)

如果它在B::Update()内崩溃,那么它告诉我Timer::ref().Cancel()即使在完全取消另一个线程中的计时器之前也会返回,包括计时器是否正在执行计划的功能。如果您可以更改Timer Cancel()的工作方式,或者为其添加其他方法,我会确保您的ID真正取消/完成计时器。这样,销毁A将调用~B(),这将取消定时器,并且永远不会调用Update(),或者至少在Update()完成之前不会返回,如果它在中间。

但是假设你不能这样做,那么我唯一能想到的就是在Bstd::shared_ptr A,并给线程std::weak_ptr到那个B,并尝试从Timer的预定函数回调中的shared_ptr获取weak_ptr。这样B::Update()只会在B对象处于活动状态时被调用,并且会在Update()期间保持活动状态。然后,您的下一个问题就是让A保持活跃状态​​,因为Update()会调用m_pA->DoSomething();B也可以weak_ptr来处理A A并使B成为共享指针对象。但是,任何一个问题都是你在std::weak_ptr的构造上启动计时器,而在C ++ 17之前,你在构造期间无法从共享对象中获得Schedule() 。因此,您必须A移动class A; class B : public std::enable_shared_from_this<B> { public: B() = default; ~B() { Timer::ref().Cancel(m_TimerId); } void Schedule(const std::shared_ptr<A>& aPtr); void Update(); private: std::weak_ptr<B> weak_from_this() { return shared_from_this(); } private: std::weak_ptr<A> wkA; int32_t m_TimerId{0}; }; void B::Schedule(const std::shared_ptr<A>& aPtr) { wkA = aPtr; m_TimerId = Timer::ref().Schedule(1000, [myself = weak_from_this()]() { if (auto me = myself.lock()) { me->Update(); } }); } void B::Update() { if (auto pA = wkA.lock()) { pA->DoSomething(); } } class A : public std::enable_shared_from_this<A> { public: void Create() { m_B = std::make_shared<B>(); m_B->Schedule(shared_from_this()); } void DoSomething() {} private: std::shared_ptr<B> m_B; };

这样的事情:

A

请注意,上面做了一些只有在C ++ 17之前才需要的东西。此外,这意味着您的$ cordova platform remove android $ cordova platform add android@3.7.1 类的用户现在必须将其创建为共享对象,这可能是不合需要的。