这里派生的 class 指针可能存在生命周期问题吗?

Is there a possible lifetime issue with the derived class pointer here?

#include <memory>
#include <iostream>
#include <thread>

class IA
{
    public:
        virtual ~IA() = default;
        virtual void foo(void) = 0;
};

class A : public IA
{
    private:
        int x;
    
    public:
        A(int _x) : x(_x) {}
        void foo(void) { std::cout << "Hello\n"; }
};

int main(void)
{
    std::unique_ptr<IA> ia_ptr = std::make_unique<A>(10);
    auto t1 = std::thread(&A::foo, dynamic_cast<A*>(ia_ptr.get()));
    
    t1.join();
    return 0;
}

这是我正在处理的一段代码的简化视图。 我需要使用线程调用函数 foo(派生 class A::foo 中的实现)。

由于派生对象 class 只能通过 unique_ptr 访问(设计约束)到基础 class,因此我将线程对象称为:

std::thread t1 = std::thread(&A::foo, dynamic_cast<A*>(ia_ptr.get()));

我的第一个问题是,由于 A 对象的生命周期与程序的生命周期一样长(因为本例中的 unique_ptr 仅在程序退出时被销毁),所以以我上面显示的方式调用线程对象是否存在任何 lifetime/dangling 指针类型风险?

我的第二个问题是,由于foo()只在A::foo中实现,所以std::thread(&A::foo, dynamic_cast<A*>(ia_ptr.get()));std::thread(&IA::foo, ia_ptr.get());在概念上有什么区别吗?在我看来,在这两种情况下,使用 A.

的相同实例调用 foo() 的相同实现

这个解释正确吗?

仅供参考,代码使用这两种方法编译。

首先,A::foo()应该标记为override:

class A : public IA
{
    ...
    
    public:
        ...
        void foo(void) override { ... }
};

就是说,在这种情况下,使 std::thread 使用 type-casted IA*-to-A* 指针直接调用 A::foo() 会破坏使用 IA 的目的。您也可以完全摆脱 IA 并单独使用 A

auto a_ptr = std::make_unique<A>(10);
auto t1 = std::thread(&A::foo, a_ptr.get());

否则,如果你想使用 IA 那么你应该实际使用它。使用原始的 IA* 指针改为 std::thread 调用 IA::foo(),并让多态性将调用分派给 A::foo() 正常:

std::unique_ptr<IA> ia_ptr = std::make_unique<A>(10);
auto t1 = std::thread(&IA::foo, ia_ptr.get());

或者,更好的是,根本不让 std::thread 直接调用 foo()。请改用 lambda,让编译器计算出正确的分派来为您调用 foo()

std::unique_ptr<IA> ia_ptr = std::make_unique<A>(10);
auto t1 = std::thread([&](){ ia_ptr->foo(); });

不,这里没有悬垂指针问题,因为正如您所说,std::unique_ptr 在线程使用完 之后被销毁。