我有一个类层次结构如下
#include <iostream>
#include <memory>
class A
{
public:
virtual void Print() = 0;
};
class B : public A
{
public:
void Print()
{
std::cout<<"Hello, world\n";
}
};
void Printer(std::unique_ptr<A> aPtr)
{
aPtr->Print();
}
int main()
{
std::unique_ptr<B> bPtr = std::make_unique<B>();
Printer(std::unique_ptr<A>(bPtr.get())); // Causing double deletion
}
如果我没有和
unique_ptrs
一起工作,解决方案是显而易见的。在函数调用期间从 B*
到 A*
的转换将隐式发生,并且将使用动态调度在 Printer
函数中调用适当的方法。
虽然,当我使用
unique_ptrs
时,编译器抱怨说 std::unique_ptr<A>
类型的参数不能被 std::unique_ptr<B>
类型的参数初始化。
在上述代码中,当我尝试从
std::unique_ptr<B>
对象 bPtr
获取原始指针并将其包装在 std::unique_ptr<A>
中时,我遇到双重删除问题,因为 bPtr
的对象占用的内存被释放第一次是Printer()
函数返回,另一次是main()
函数返回。
有没有办法解决这个问题,以便我从
std::unique_ptr<B>
到 std::unique_ptr<A>
进行正确的转换?
首先,
void Printer(std::unique_ptr<A> aPtr)
对于打印值的函数来说是一个非常不寻常的签名。按值获取唯一指针意味着函数想要接管所有权,但这对于只想打印一些东西的函数来说是没有意义的。
请记住,唯一指针是关于唯一所有权的。只能有一个唯一的指针管理一个对象的生命周期。因此你不能复制一个唯一的指针。
当不需要所有权转移时,原始指针是可以的:
void Printer(A* aPtr)
{
aPtr->Print();
}
Printer(bPtr.get());
或者简单地传递 const 引用,就像如果对象不是由唯一指针管理的那样。
void Printer
应该不需要关心对象是动态分配的还是由唯一的ptr管理的。
另一方面,如果函数确实接管了所有权并且确实按值获取了唯一指针,那么您必须显式地从原始指针移动:
void Printer(std::unique_ptr<A> aPtr);
std::unique_ptr<B> bPtr = std::make_unique<B>();
Printer(std::move(bPtr));
PS:请注意,接受原始指针的
std::unique_ptr
的构造函数是explicit
,以防止意外转移所有权。你必须明确地调用构造函数,否则它不会编译,这很好。
unique_ptr 是关于单一所有权的。我不认为你的打印机功能的目标是这样做。我认为你只能这样修复你的代码:
void Printer(A* aPtr) { aPtr->Print(); }
int main() {
std::unique_ptr<B> bPtr = std::make_unique<B>();
Printer(bPtr.get()); // no double deletion
}