在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());
}
我的问题是,如果像我们在这里看到的那样,智能指针一般不能转换为指针,那么智能指针使用什么机制来允许运行时多态性呢?
智能指针是显式设计的,以使这种转换成为可能。 在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()
中构造是错误的,因为您可能会有其他实例具有相同的共享状态,而您没有传输这些状态。 因此,可能会出现两个指向同一指针的智能指针,但共享状态不同。