Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart pointers and dynamic_cast

I apologize in advance if this was answered already as I looked and could not find the answer.

NOTE: this IS a homework assignment, so if you feel uncomfortable answering, I completely understand.

I have the following:

ptr.h:

template<typename T>
class Ptr {
    T* address;
    size_t* counter;
    Ptr(T* address) : address(address), counter(new size_t(1)) { }
    Ptr(const Ptr& other) : address(other.address), counter(other.counter) 
    { 
        ++(*counter); 
    }
    virtual ~Ptr() { 
        if (0 == --(*counter)) { delete address; delete counter; } 
    }
    Ptr& operator=(const Ptr& right) {
        if (address != right.address) {
            if (0 == --(*counter)) { delete address; delete counter; }
            address = right.address;
            counter = right.counter;
            ++(*counter);
        }
        return *this;
    }
    T& operator*()  const { TRACE(address); return *address; }
    T* operator->() const { TRACE(address); return address;  }
    T* raw()        const { TRACE(addr); return addr;  }
};

main.cc:

#include <iostream>
#include "ptr.h"

using std::cout;
using std::endl;


class Base {
public:
    Base()  { std::cout << "Base()" << std::endl; }
    virtual ~Base() { std::cout << "~Base()" << std::endl; }
    virtual std::string str() { return "In Base::str()"; }
};

class Derived: public Base {
public:
    Derived()  { std::cout << "Derived()" << std::endl; }
    ~Derived() { std::cout << "~Derived()" << std::endl; }
    std::string str() { return "In Derived::str()"; }
};



int main() {
Ptr<Base> base(new Base());
Ptr<Derived> derived(new Derived());

Ptr<Base> pbase(0);
Ptr<Derived> pderived(0);

    // upcasting can be done like this, but is it the best way?
    pbase = *((Ptr<Base>*)(&derived));
    cout << pbase->str() << endl;   // outputs: "In Derived::str()"

    /* 
     * downcasting should be done using dynamic_casts
     * but how can I downcast here? 
     * what do I know so far:
     * 1. just because Derived is a subclass of Base does not mean Ptr<Derived> is a 
     * subclass of Ptr<Base> so there is no hierarchy between the two so I cannot 
     * really use dynamic_casts here
     * 2. The dynamic_cast I do use is sort of useless no? since pbase is a Ptr<Base>
     */
    pderived = *((Ptr<Derived>*)(dynamic_cast<Ptr<Base>*>(&pbase)));
    cout << pderived->str() << endl;


return 0;
}

Now, the goal was to use dynamic_cast to go back and forth, and although I found many interesting tidbits about smart pointers nothing really explained how this is implemented.

I tried just getting the address field for pbase, and then initializing pderived to a new Ptr with that address but of course my reference counts got all screwed up.

I tried creating a new Ptr that held a reference to the counter of pderived but then I couldn't set the address field of pderived so I got stuck there too.

I'm telling you guys this information because: 1. I want to make a point that I've been working on this for quite some time before asking help online and 2. I want you to know what I've tried already.

I could really use some advice here. Just how to get:

pderived = <SOMETHINGSOMETHING>pbase<SOMETHINGSOMETHING>

Thanks!

like image 664
mlnyc Avatar asked Apr 21 '12 23:04

mlnyc


1 Answers

Typically the smart pointer class will expose a dynamic cast wrapper that deals with the underlying smart pointer object properly. For example, C++0x has the dynamic_pointer_cast function. Note that your *((Ptr<Derived>*)(dynamic_cast<Ptr<Base>*>(&pbase))); can and will break, particularly if you have multiple inheritance, as it won't adjust the inner pointer at address to the right offset within the subclass to get at whatever superclass is needed.

At the implementation level, a refcounting smart pointer class will usually carry two pointers in each smart pointer; one points directly the the object (properly casted to whatever type the pointer is), and one points to a structure which contains a reference count, a pointer to the original object, and a pointer to a deletion routine. This allows any casted variant of the pointer to correctly get back the original object and call its destructor. Currently you're pointing straight to a size_t, which doesn't give you this information.

In any case, once you have this indirection for the destructor, when it comes time to cast these smart pointers, then, you can cast only the outer pointer; leaving this inner reference-counting structure alone for when destruction time rolls around.

Actually turning this all into code, of course, is left as an exercise for the reader :)

like image 127
bdonlan Avatar answered Sep 20 '22 20:09

bdonlan