简体   繁体   中英

C++ - Cast/Change-type of an unique_ptr

Edit: Thanks, amazing help, as always :)

I can't find the solution to this, I have an unique_ptr to a base class, that has the data of a derived class, and I want to set it's type to the derived class so I can access the derived members. See this code:

#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;
}

The type of b1 is Base but it's data contains the data of Derived. See:

问题的屏幕截图。我们可以看到对应于Derived的数据字节

Is it hard to get ? I thought about manipulating memory bytes, like saying [b1+4] (pseudo thoughts) and accessing it, but I think about complicated objects, because I'm doing an entity system for a game, I can't do this :(

Thanks !

Your options are:

1) Cast C-like pointer (recommended)

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

2) Implement your own static_unique_ptr_cast where you delete b1 and create a new 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()));
}

The function takes rvalue reference to ensure you're not stealing the resource from lvalue. Usage:

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

Note: If you think you need to use 2) I would consider your design being flawed. The cast is not in the STL for some reason.

Edit: The static_unique_ptr_cast now keeps the deleter.

As Sneftel suggested, you can simply cast the pointer contained within the unique_ptr in order to access the other methods.

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

Keep in mind that this seems strange though. Generally if you have access to a pointer of a given type, you should not be casting it in the hope it has the type you have in mind. Instead, if you need to access methods of a given subclass, you should require that your inputs are pointers of that subclass. Storing base classes in smart pointers is generally done to leverage polymorphism, not much else.

If you are not using polymorphism, consider using a unique_ptr with the derived class in the template, and then downcasting that to the base pointer when just the base class interface is needed.

Another option is to keep the same pointer stored in two places: one as a Base , and one as a Derived - keeping in mind you'll have to use smart pointers only one one of those!

You can use the shared pointer casts if you use shared_ptr

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

your whole question is explained on the cppreference site: http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

I would be careful of the other suggestions, the standard did think of the issue and implemented a clean solution.

I am not sure that I understand what you are trying to do, but it looks more like a design matter.

If you have a pointer to Base, the actual object might be a Base or a derived class (not necessary Derived). That is, you cannot cast it to Derived without making some dangerous assumptions.

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM