6

I'd like to add the extra functionality without changing the existing class.

Say,

class base{
public:
    int i;
    base(){i = 1;}
    virtual void do_work(){ /*Do some work*/ }
};

If I want to add serialization member function to it, I will simply create a derived class

class derived : public base{
public:
    void serialize();
};

void derived::serialize(){
    cout << "derived class" << endl;
}

And I do need to handle existing base objects,e.g.

int main(){
    base a;
    derived & b = static_cast<derived &>(a);

    b.serialize();
}

This example runs without problems. But I do know the downcast through static_cast is something to be avoided in general.

But I'd like to know if the downcast for this particular use case can be considered safe since the derived class only has one extra member function. Will it has some potential undefined behavior for accessing vtable?

kenan238
  • 398
  • 4
  • 13
Mike Jiang
  • 209
  • 1
  • 2
  • 10
  • 1
    What `vtable` are you talking about? I don't see any virtual functions. – James Adkison Mar 25 '16 at 20:49
  • 2
    Possible duplicate of [Downcasting using the Static\_cast in C++](http://stackoverflow.com/questions/6322949/downcasting-using-the-static-cast-in-c) – Chad Mar 25 '16 at 20:51
  • Also, `derived` `IS-A` `base`, not the other way around, but you're casting `a` (i.e., a `base` type) to a `derived` type ... – James Adkison Mar 25 '16 at 20:51
  • The issue I see is that we do not know if the base class has a virtual dtor. This could lead to issues when dealing with objects on the heap. I would make a template function to handle this. This way you get compile time warnings if you pass an object that doesn't support the serializable method. – Freddy Mar 25 '16 at 20:53
  • @JamesAdkison, virtual function is added back as I originally meant. The situation is I have `base` object created by another program that does not know `derived`. The reason I downcast the `base` is I need to serialize it by the new `function` added in `derived` – Mike Jiang Mar 25 '16 at 22:07

3 Answers3

5

The way you're extending Base you're not making use of the vtable because you have no virtual methods. It may be easier to think of it as Derived has A Base; That you created a new class that contains a Base member variable.

My Suggestion.

Template Function

I personally would go with a template function. You can keep all the work in your original question, and avoid the need of adding virtual calls to your class.

template<typename T>
void serialize_object(T& t)
{
  t.serialize()
}

And then based on your example.

Derivied d;
serialize_object(d);

The big benefit is that you're not adding runtime cast here. The compiler will inform you if you pass an object that doesn't have a method serialize.

Go Virtual

If you really want to handle this through a virtual interface do so.

struct Serializable{
 virtual void serialize()=0;
 virtual ~Serializable(){}
}

class Derived : public Serializable {
  public:
   void serialize() override;
}

void Derivied::serialize()
{
 std::cout << "Yah\n";
}

Then in your code.

Derivied d;
Serializable& s = dynamic_cast<Serializable&>(d);

However, the big concern here is who is the owner of your base class? Did they provide a virtual dtor? If not, then making use of std::unique_ptr or std::shared_ptr could cause you to not deal directly with the interface.

Freddy
  • 2,249
  • 1
  • 22
  • 31
  • 1
    Why would you cast `Derived` to `Serializable`? Isn't `serialize()` function already available in `Derived`? – Mike Jiang Mar 25 '16 at 22:27
  • 1
    @MikeJiang you're correct, there really is no reason to cast in this case. In this scenario I would prefer to write a common function that deals with a generic interface like `serialization` or a template function. – Freddy Mar 28 '16 at 20:13
3

If you can write the serialize function in a derived class without touching private or protected members then you should simply make it a free function. That solves everything.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • There are other derived classes inherited from `base` in my application. And I need to do the same serialization for them. All these derived objects are stored as `base *` so free function can't achieve the polymorphic function calls as member function ((i.e `obj.serialze()`). – Mike Jiang Mar 25 '16 at 22:15
0

You can't just cast a base class object to an inherited class. Any members in the inherited class will not have been created and initialized. You need to start with an object of the inherited class.

You can go the other way, of course, and call base class functions from a function of a class inherited from the base, because the base class object will always be there as part of the inherited class object, by definition.

If you have a pointer to the base class, and you build with RTTI, you can use dynamic_cast to cast to an inherited class, but that cast will fail and return NULL if the object is not of the class you're trying to cast to. But usually it's better to call a virtual function than to use dynamic_cast.

Dan Korn
  • 1,274
  • 9
  • 14
  • 2
    You need some virtual functions if you're going to be using `dynamic_cast`, see [FAQ: Why does dynamic_cast only work if a class has at least 1 virtual method?](http://stackoverflow.com/questions/4227328/faq-why-does-dynamic-cast-only-work-if-a-class-has-at-least-1-virtual-method). – James Adkison Mar 25 '16 at 20:54