C++ - unique_ptr 的转换/更改类型

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

编辑:谢谢,一如既往的惊人帮助:)

我找不到解决方案,我有一个基类的 unique_ptr,它具有派生类的数据,我想将其类型设置为派生类,以便我可以访问派生成员。看这段代码:

#include <memory>
#include <iostream>

class Base
{
public:
    int v1 = 0xAAAAAAAA;
};

class Derived : public Base
{
public:
    int v2 = 0xBBBBBBBB;
};

int main()
{

    std::unique_ptr<Base> b1(new Derived); //How to access b1->v2 ?
    std::unique_ptr<Base> b2(new Base);


    std::getchar(); 
    return 0;
}

b1 的类型是 Base,但它的数据包含 Derived 的数据。 参见:

很难买到吗?我想过操纵内存字节,比如说 [b1+4] (伪思想)并访问它,但我想到了复杂的对象,因为我正在为游戏做一个实体系统,我不能这样做:(

谢谢!

c++ pointers types unique-ptr
5个回答
5
投票

您的选择是:

1)转换类C指针(推荐)

std::unique_ptr<Base> b2(new Base);
Derived * p = static_cast<Derived *>(b2.get());

2) 实现您自己的

static_unique_ptr_cast
,删除
b1
并创建新的
std::unique_ptr
:

template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del> 
static_unique_ptr_cast(std::unique_ptr<Base, Del> && p)
{
    auto d = static_cast<Derived *>(p.release());
    return std::unique_ptr<Derived, Del>(d, std::move(p.get_deleter()));
}

该函数采用右值引用以确保您不会从左值窃取资源。用途:

std::unique_ptr<Base> b1(new Derived);
std::unique_ptr<Derived> p = static_unique_ptr_cast<Derived>(std::move(b1));

注意:如果你认为你需要使用2)我会认为你的设计有缺陷。由于某种原因,演员阵容不在 STL 中。

编辑:

static_unique_ptr_cast
现在保留删除器。


1
投票

如果使用shared_ptr,则可以使用共享指针强制转换

std::static_pointer_cast<Derived>(b1)->v2;

您的整个问题在 cppreference 站点上进行了解释:http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

我会小心其他建议,该标准确实考虑到了这个问题并实施了一个干净的解决方案。


1
投票

正如 Sneftel 所建议的,您可以简单地转换

unique_ptr
中包含的指针来访问其他方法。

static_cast<Derived*>(b1.get())->v2

请记住,这看起来很奇怪。一般来说,如果您有权访问给定类型的指针,则不应希望它具有您想要的类型而对其进行强制转换。相反,如果您需要访问给定子类的方法,则应该要求您的输入是该子类的指针。在智能指针中存储基类通常是为了利用多态性,仅此而已。

如果您不使用多态性,请考虑在模板中使用带有派生类的

unique_ptr
,然后在仅需要基类接口时将其向下转换为基指针。

另一种选择是将相同的指针存储在两个位置:一个作为

Base
,一个作为
Derived
- 请记住,您只能使用其中一个智能指针!


1
投票

我不确定我是否理解你想要做什么,但它看起来更像是一个设计问题。

如果您有一个指向 Base 的指针,则实际对象可能是 Base 或派生类(不一定是 Derived)。也就是说,如果不做出一些危险的假设,您就无法将其转换为 Derived。

class B{public:virtual ~B(){}};
class D : public B{};
class DD : public B{};

int main()
{
  std::unique_ptr< B > pb( new D );
  D* pd = dynamic_cast< D* >( pb.get() );
  DD* pdd = dynamic_cast< DD* >( pb.get() ); // null
  return 0;
}

0
投票

我无法使提议的

static_unique_ptr_cast
发挥作用。这是一个工作版本:

#include <iostream>
#include <memory>

class Base {
public:
    virtual void Do() { std::cout << "Base::Do" << std::endl; }
};

class Derived: public Base {
public:
    void Do() override { std::cout << "Derived::Do" << std::endl; }
};

template<typename T>
void destroy_der(T* p) {
    auto der = dynamic_cast<Derived*>(p);
    std::cout << "destroy_derived" << std::endl;
    delete der;
}

template<typename Derived, typename Base>
std::unique_ptr<Derived>
static_unique_ptr_cast(std::unique_ptr<Base> && p)
{
    auto d = dynamic_cast<Derived *>(p.release());
    return std::unique_ptr<Derived>(d);
}

template<typename Derived, typename DelDerived, typename Base, typename DelBase>
std::unique_ptr<Derived, DelDerived>
static_unique_ptr_cast_del(std::unique_ptr<Base, DelBase> && p)
{
    auto d = dynamic_cast<Derived *>(p.release());
    return std::unique_ptr<Derived, DelDerived>(d, reinterpret_cast<DelDerived>(std::move(p.get_deleter())));
}

int main(void) {

    {
        std::unique_ptr<Base> b(new Derived);
        std::unique_ptr<Derived> d = static_unique_ptr_cast<Derived>(std::move(b));
        d->Do();
    }
    {
        std::unique_ptr<Base, void(*)(Base*)> b(new Derived, destroy_der<Base>);
        std::unique_ptr<Derived, void(*)(Derived*)> d = static_unique_ptr_cast_del<Derived, void(*)(Derived*)>(std::move(b));
        d->Do();
    }

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.