这里派生的 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
在线程使用完 之后被销毁。
#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
在线程使用完 之后被销毁。