unique_ptr 到派生类作为函数的参数,该函数将 unique_ptr 带到基类

Posted

技术标签:

【中文标题】unique_ptr 到派生类作为函数的参数,该函数将 unique_ptr 带到基类【英文标题】:unique_ptr to a derived class as an argument to a function that takes a unique_ptr to a base class 【发布时间】:2013-07-02 16:08:18 【问题描述】:

我正在尝试在将unique_ptr 用于基类的函数中使用unique_ptr 派生类。比如:

class Base ;

class Derived : public Base ;

void f(unique_ptr<Base> const &base) 

…

unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived);
f(derived);

如果我正确理解this answer,这段代码应该可以工作,但会导致以下编译错误:

错误 C2664: 'f' : 无法将参数 1 从 'std::unique_ptr<_ty>' 转换为 'const std::unique_ptr<_ty> &'

IntelliSense:不存在从“std::unique_ptr>”到“const std::unique_ptr>”的合适的用户定义转换

如果我将f 更改为unique_ptr&lt;Derived&gt; const &amp;derived,它可以正常工作,但这不是我想要的。

我做错了吗?我可以做些什么来解决这个问题?

我正在使用 Visual Studio 2012。

【问题讨论】:

【参考方案1】:

您有三个选择:

    放弃所有权。这将使您的局部变量在函数调用后无法访问动态对象;对象已转移给被调用者:

    f(std::move(derived));
    

    更改f的签名:

    void f(std::unique_ptr<Derived> const &);
    

    更改变量的类型:

    std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);
    

    或者当然只是:

    std::unique_ptr<base> derived(new Derived);
    

    甚至:

    std::unique_ptr<base> derived = std::make_unique<Derived>();
    

    更新:或者,按照 cmets 的建议,根本不要转让所有权:

    void f(Base & b);
    
    f(*derived);
    

【讨论】:

在这种情况下,我正在考虑使用 4。使用 shared_ptr 在函数调用中使用引用而不是 unique_ptr 怎么样? @svick,如果它不拥有指针的所有权,为什么还要将智能指针传递给函数?这不是智能指针的用途。 @metal:太好了,谢谢 - 是的,重要的细节,返回类型必须匹配。我错过了。好东西。你可以说return std::unique_ptr&lt;Base&gt;(std::move(p)),我想。 “这将在函数调用结束时销毁对象”:但是我们不知道f()在做什么(假设它实际上不是空的) - 它可能会将所有权转移到其他地方。我们不能说我们放弃或转让所有权吗?【参考方案2】:

我有接受答案的选项#1,但我仍然遇到相同的编译错误。我把头撞在墙上一个多小时,我终于意识到我有

class Derived : Base ;

而不是

class Derived : public Base ;

【讨论】:

【参考方案3】:

一个可能的解决方案是将参数的类型更改为Base const*,并改为传递derived.get()unique_ptr const&lt;Base&gt;&amp; 没有所有权转移(并且unique_ptr 没有被修改),因此更改为Base const* 不会改变含义。


Herb Sutter 在Smart Pointer Parameters 中详细讨论了传递智能指针参数。链接文章的摘录指的是这种确切的情况:

传递const unique_ptr&lt;widget&gt;&amp; 很奇怪,因为它只能接受nullwidget,其生命周期恰好通过unique_ptr 在调用代码中进行管理,而被调用者通常不应该关心呼叫者的终生管理选择。传递 widget* 涵盖了这些情况的严格超集,并且可以接受“nullwidget”,而不管调用者使用的生命周期策略如何。

【讨论】:

Base const* 不是意味着该函数可以将指针存储在某处吗?我虽然智能指针试图避免这种情况。 @svick,该函数可以同样轻松地存储base.get()get()const 成员函数)。 @svick 否。智能指针通过明确谁是对象的所有者来避免内存泄漏。如果您将原始指针作为参数传递给函数,您只需授予读取/修改它的权限。智能指针是为了避免使用原始的newdelete,而不是指针。【参考方案4】:

另一种方法是更改​​f 的签名并以稍微不同的方式使用它:

void f(Base* base_ptr) 
    // take ownership inside the function
    std::unique_ptr<Base> base base_ptr;
    // ...


// ...
auto derived = std::make_unique<Derived>();
f(derived.release());  // release ownership

【讨论】:

以上是关于unique_ptr 到派生类作为函数的参数,该函数将 unique_ptr 带到基类的主要内容,如果未能解决你的问题,请参考以下文章

如何使 Python 模拟从基类派生?

为啥当我有两个函数时编译器没有显示错误,一个将基类作为参数,一个将派生类作为参数?

如何派生抽象模板类,模板类型作为函数参数 (C++11)

派生类的模板类作为函数的参数 - 危险?

是否可以通过引用以基类作为参数的函数来传递派生类

比较 std::unique_ptr 指向的底层(派生)类型