提问者:小点点

派生类的智能指针如何隐式地转换到基类?


在cppreference中,

如果T是某个基B的派生类,则std::unique_ptr隐式可转换为std::unique_ptr

为了使多态性像处理原始指针一样工作,显然必须这样做。 我的问题是,如果像我们在这里看到的那样,智能指针一般不能转换为指针,那么智能指针使用什么机制来允许运行时多态性呢? 我的想法是,在构造函数或std::make_unique<>()/std::make_shared<>()中,对象中的内部指针用于此转换。 但是,如果在其他任何地方都不允许这些隐式转换,为什么我们在构造智能指针时不必调用get()呢?

作为一个非常简单的例子,我想出了以下测试:

#include <iostream>
#include <memory>

class Base
{
public:
    virtual ~Base() = default;

    virtual void foo() const { std::cout << "Base foo() called." << std::endl; }
};

class Derived : public Base
{
public:
    virtual void foo() const override { std::cout << "Derived foo() called." << std::endl; }
};

void bar(Base* pBase) 
{
    std::cout << "bar() called." << std::endl;
    pBase->foo();
}

int main()
{
    std::unique_ptr<Base> pObject { std::make_unique<Derived>() };      // Implicit conversion here, why no call to get()?

    // bar(pObject);                                                    // Can't be converted, so we have to call get()
    bar(pObject.get());
}

共2个答案

匿名用户

我的问题是,如果像我们在这里看到的那样,智能指针一般不能转换为指针,那么智能指针使用什么机制来允许运行时多态性呢?

智能指针是显式设计的,以使这种转换成为可能。 在std::unique_ptr构造函数文档中可以看到:

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept; (6)

这个重载就是为此目的创建的。

此构造函数仅在以下所有条件均为真的情况下参与重载解析:

a)unique_ptr::pointer隐式可转换为指针

b)U不是数组类型

c)Deleter是引用类型并且E与D是相同类型,或者Deleter不是引用类型并且E可以隐式地转换为D

重点是我的。 因此,由于指向派生类的指针可以隐式地转换为基类,因此构造函数重载使得这种转换成为可能。

匿名用户

.get()返回的指针不会将所有权转移给调用方(即.release())。

如果您使用.get()中的指针来构造另一个智能指针,您将得到一个双空指针。

因此这是一个错误:

std::unique_ptr<Base> pObject { std::make_unique<Derived>().get() };

这个管用

std::unique_ptr<Base> pObject { std::make_unique<Derived>().release() };

对于shared_ptr,同时从.get().release()中构造是错误的,因为您可能会有其他实例具有相同的共享状态,而您没有传输这些状态。 因此,可能会出现两个指向同一指针的智能指针,但共享状态不同。