使用包含在 std::unique_ptr 中的对象进行多态数据类型转换

问题描述 投票:0回答:2

我有一个类层次结构如下

#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>
进行正确的转换?

c++ memory-management memory-leaks stl unique-ptr
2个回答
3
投票

首先,

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
,以防止意外转移所有权。你必须明确地调用构造函数,否则它不会编译,这很好。


0
投票

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
}

现场演示

© www.soinside.com 2019 - 2024. All rights reserved.