Andrew Marshall
Andrew Marshall

Reputation: 96904

Casting from base class to unknown derived class

Given an object initialized like so:

Base* a = new Derived();
Container<Base> c(a);

where

class Base {
  ...
  protected:
    ~Base();
}

class Derived : public Base {...};

template <typename T>
class Container {
  private:
    T* object;

  public:
    Container(T* o) : object(o) {}
    void deleteObject() {
      delete object;  // Object must be casted to (unknown) derived type to call destructor.
    }
};

Obviously this is very simplified from the actual code, but the question is how do I cast object from its templated type to its actual, derived type (if they are different), which is not known?

I cannot modify Base or Derived, or even any of the code calling Container, only the Container class itself.

Upvotes: 1

Views: 1921

Answers (5)

Puppy
Puppy

Reputation: 146910

You need to template the constructor and store a type-erased deleter. This is how shared_ptr does it.

template <typename T>
class Container {
  private:
    T* object;
    std::function<void(T*)> deleter;
  public:
    template<typename U> Container(U* o) : object(o) {
        deleter = [](T* ptr) { delete static_cast<U*>(ptr); };
    }
    void deleteObject() {
        deleter(object);
    }
};

Upvotes: 4

lapk
lapk

Reputation: 3918

EDIT: I didn't realize you cannot modify Container signature as well...

If you can modify your deleteObject:

template <typename T>
class Container {
  private:
    T* object;

  public:
    Container(T* o) : object(o) {}
    template< typename PDerived >
    void deleteObject() {
      delete static_cast< PDerived* >( object ); 
    }
};

Base* a = new Derived();
Container<Base> c(a);

c.deleteObject<Derived>();

EDIT: Someone posted same solution earlier.

Upvotes: 0

parapura rajkumar
parapura rajkumar

Reputation: 24403

If you cannot change both the Base or Derived or make the destructor virtual, you can make the deleteObject a template function

template <typename T>
class Container {
  private:
    T* object;

  public:
    Container(T* o) : object(o) {}


    template <typename U>
    void deleteObject() {
      U* c = static_cast<U*>(object);
      delete c;
    }
};


int main(void)
{
    Base* a = new Derived();
    Container<Base> *b = new Container<Base>(a);


    b->deleteObject<Derived>();
        return 0;
}

Upvotes: 0

Xeo
Xeo

Reputation: 131779

If you're able to change the creation code, you might get away with this:

template<class T>
void deleter(void* p){
  delete static_cast<T*>(p);
}

template<class T>
class Container{
private:
  T* obj;
  typedef void (*deleter_func)(void*);
  deleter_func obj_deleter;

public:
  Container(T* o, deleter_func df)
    : obj(o), obj_deleter(df) {}
  void deleteObject(){ obj_deleter(obj); }
};

And in the calling code:

Base* a = new Derived();
Container<Base> c(a, &deleter<Derived>);

Upvotes: 2

johnsyweb
johnsyweb

Reputation: 141770

Hint (since this is homework): Look up the keyword virtual.

Upvotes: 0

Related Questions