SmartPointer : cast between base and derived classes
Asked Answered
S

4

6

Say you have a function like this :

SmartPtr<A> doSomething(SmartPtr<A> a);

And classes like this :

class A { }
class B : public A { }

And now I do this :

SmartPtr<A> foo = new B();
doSomething(foo);

Now, I would like to get back a SmartPtr<B> object from doSomething.

SmartPtr<B> b = doSomething(foo); 

Is it possible ? What kind of casting do I have to do ?
Right now, I just found something I believe ugly :

B* b = (B*)doSomething().get()

Important notes : I do not have any access to SmartPtr and doSomething() code.

Suzisuzie answered 12/4, 2011 at 10:20 Comment(3)
Do you know where the SmartPtr comes from? Any documentation?Quant
Any smart pointer library worth its salt will provide a specialised cast function to do this. For example in boost with shared_ptr its boost::shared_ptr<A> b = boost::dynamic_ptr_cast<B>(foo) Look for something like that.Overcast
Well this is a very basic SmartPtr library and there nothing that allows dynamic casting.Suzisuzie
V
8

Instead of doing that, you can do this :

B *b = dynamic_cast< B* >( doSomething.get() );

but you have to check if b is NULL.

Versus answered 12/4, 2011 at 10:22 Comment(5)
It is advised to always use dynamic_cast when casting upwards in the class hierarchy and check for NULL every time. Unless you can use static cast which means the compiler figures out that you can actually do the cast because the pointer points to a B* object. Keep in mind that dynamic_cast requires RTTI which will increase the output file size.Moisten
... but hand-rolled alternatives to RTTI will also increase the file size. In general, functionality increases file size. The big advantage of the standard RTTI is that it's been tested extensively and optimized.Malamute
Thanks for your answers. But remember, I would like a SmartPtr<B> and not a B*.Suzisuzie
In fact I am using an external library and I cannot modify the SmartPtr implementation. But, you finally answerd my question : I cannot cast from SmartPtr<A> to SmartPtr<B>, even if B is derived from A, if SmartPtr does not give some way to do the cast. Am I right ?Suzisuzie
@Suzisuzie That depends on SmartPtr, but most likely you are right, because SmartPtr should release the object, unless it does something smart.Antenatal
S
6

For anyone stumbling across this decade old question while looking for how to do this, C++11 added dynamic_pointer_cast, static_pointer_cast, and const_pointer_cast to do exactly this. Now this problem is as simple as

shared_ptr<B> b=static_pointer_cast<B>(doSomething(foo));
Shopwindow answered 15/6, 2022 at 5:23 Comment(0)
L
-1

You can define your own SmartPtrCast template function which does something like:

template <typename DestT, typename SrcT>
inline SmartPtr<DestT> SmartPtrCast(const SmartPtr<SrcT> &src)
{
    return SmartPtr<DestT>(static_cast<DestT*>(src.get()));
}

Then, all you have to do cast gracefully from A to B is:

SmartPtr<B> b = SmartPtrCast<B>(doSomething(foo));

Caveat Emptor: This will only work if the smart pointer returned by doSomething() is referenced somewhere else, and is not destroyed when it goes out of scope. Judging by your example, this is the case, but it's still not as graceful, and it should be noted that the two pointers won't share their reference counting (so if one of them gets destroyed, the second will lose its data).

A better solution is either to detach one of the pointers (if SmartPtr has a detach method). An even better solution (if you don't have a detach method or if you want to share the reference count) is to use a wrapper class:

template <typename SrcT, typename DestT>
class CastedSmartPtr
{
private:
    SmartPtr<SrcT> ptr;
public:
    CastedSmartPtr(const SmartPtr<SrcT>& src)
    {
        ptr = src;
    }

    DestT& operator* () const
    {
        return *(static_cast<DestT*> >(ptr.get()));
    }

    DestT* operator->() const
    {
         return static_cast<DestT*> >(ptr.get());
    }

    DestT* get() const
    {
        return static_cast<DestT*> >(ptr.get());
    }
}

template <typename DestT, typename SrcT>
inline SmartPtr<DestT> SmartPtrCast(const SmartPtr<SrcT>& src)
{
    return CastedSmartPtr<SrcT, DestT>(src);
}

This will use a SmartPtr internally (so reference count is properly shared) and static_cast it to internally to DestT (with no performance impact). If you want to use dynamic_cast, you can do it only once, in the constructor, to avoid unnecessary overhead. You may also want to add additional method to the wrapper such as copy constructor, assignment operator, detach method, etc.

Lagan answered 12/4, 2011 at 10:33 Comment(4)
This will do bad things. You'll end up double destroying the thing pointed to as you wont have both pointers sharing the same reference count. (Yes I''m assuming stuff about the implementation of the SmartPtrs ...)Overcast
Assuming B* b = (B*)doSomething().get(); or B *b = dynamic_cast< B* >( doSomething.get() ); works, this should work as just as well, which might not be the best, but if you don't have any access to the smart pointer library, this is the best you can get. A better still implementation is possible if SmartPtr detach() method or something of this sort. I'll factor that in.Lagan
Actually, the question is misformed so your answer looks acceptable.Quant
Well, on second thought I admit it might be problematic, depending on the circumstances and the SmartPtr implementation (which we don't know a whole lot about). The wrapper solution is much safer, and should not have any performance impact.Lagan
Q
-2
SmartPtr<B> b = dynamic_cast<B*>(doSomething().get())

or perhaps something like doSomething().dynamic_cast<B*>() if your SmartPtr supports it.

Quant answered 12/4, 2011 at 10:23 Comment(5)
-1, this method will cause b to have a reference count of 1 (which is generally too low, but just happens to work in this case)Malamute
Why -1? This is the best you can do given the questionQuant
Look at VJo's answer. Since you can't const construct a smart pointer with a proper fererence count, it's far safer to construct a raw pointer. Personally, I'd use a reference, that makes it even clearer that the lifetime is dictated elsewhere. But Michael Anderson's comment is even more useful - it's solved in any decent smart pointer library.Malamute
I don't agree, you are not answering the original question but changing it.Quant
This "fix" assumes that doSomething() returns something which has referencecount == 1. If someone else is holding a reference to whatever doSomething() returns, there will be two SmartPtrs referencing the same instance (hence, it will be destroyed twice).Clovis

© 2022 - 2024 — McMap. All rights reserved.