0
class Base {
virtual void func1();
}

class Derived : Base {
void func1();
void func2();
}

vector<Base *> vec;
vec.push_back(new Base()), vec.push_back(new Derived());

What is the correct/clean way to call func2 without knowing which index corresponds to which class? Is there a convention to do such a thing? I also want to avoid using typeid.

mkmostafa
  • 3,071
  • 2
  • 18
  • 47
  • 2
    You mentioned you don't know which index corresponds to which class, but still need to call `func2()`. Thus you cannot guarantee that `func2()` will be called for `Derived` objects. Which result do you expect from calling `func2()` for `Base` objects? – alexeykuzmin0 Nov 12 '15 at 10:28
  • 6
    You might want to read about [object](https://en.wikipedia.org/wiki/Object_slicing) [slicing](http://stackoverflow.com/questions/274626/what-is-object-slicing). – Some programmer dude Nov 12 '15 at 10:28
  • @alexeykuzmin0 I'd like a way to know the class type maybe? I'm open to any other solutions :) – mkmostafa Nov 12 '15 at 10:30
  • @JoachimPileborg Sorry I know about object slicing....I updated the question. – mkmostafa Nov 12 '15 at 10:32
  • 2
    Maybe a stupid question: What is the point of using a polymorphic vector when you want to use it in a non-polymorphic way? Cant you just use two vectors (one for `Base` objects and one for `Derived` obejcts) ? – 463035818_is_not_an_ai Nov 12 '15 at 10:33
  • 2
    @mkmostafa the whole point of accessing objects through a base class, is that you want to use the interface they are all guaranteed to have without having to know the specific details of the run-time types. So your question kinda subverts the whole principle of object-oriented programming (in particular, polymorphism). Now of course there are ways to find out the run-time type, but it would probably be better if you explained to us what `Base`, `Derived`, `func1` and `func2` _actually_ are in your code and we might be able to suggest a better solution. – CompuChip Nov 12 '15 at 10:34
  • Can you edit `Base` and `Derived` in your example or may they be defined out of your reach (e.g. in a 3rd party library)? – Simon Kraemer Nov 12 '15 at 10:34
  • @SimonKraemer yes I can! – mkmostafa Nov 12 '15 at 10:42
  • if you can edit the classes it is trivial, just add a `func2(){}` to the base – 463035818_is_not_an_ai Nov 12 '15 at 10:48
  • @mkmostafa your original question used `vector`, not `vector`. True, you changed it before any answer was posted, but people had already started writing them. Edits that invalidate answers aren't well accepted. That's why another user rolled your edit back. I think now all the answers are based on your updated code, so it can be fine; but in case it happens again, please do not make an edit that invalidates answers. Instead, ask a new question, maybe with a link to the original. – Fabio says Reinstate Monica Nov 12 '15 at 10:51
  • @FabioTurati Only two people posted answers based on the first revision and I replied to each one of them That I updated the question!...People started to answer based on the new updates and then someone rolls my question back?! – mkmostafa Nov 12 '15 at 11:16

3 Answers3

2

In your case the objects are sliced, as mentioned in king_nak's answer, so there's no safe way to call func2().
But you can store pointers to Base instead of Base objects - in this case you can use dynamic_cast:

std::vector<Base*> vec;
vec.push_back(new Base());
vec.push_back(new Derived());

for (auto obj : vec)
{
    Derived* d = dynamic_cast<Derived*>(obj);
    if (d)
    {
        d->func2();
    }
}

Some info on dynamic_cast: link

PS: Also, if you want to call function func2() on Base objects, I think it makes sense to add a stupid implementation to Base class and make the function virtual.

alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • @mkmostafa anyway, you can use `dynamic_cast` to solve this problem. Also, you can use `reinterpret_cast` to solve original problem with object slicing, but it's highly unsafe – alexeykuzmin0 Nov 12 '15 at 10:46
  • @alexeykuzmin0 Dynamic cast is the way to go. Everyone agreed on that ;). I do not have a problem with object slicing, just someone edited my post and changed the pointers to objects :/ Thanks anyways :) – mkmostafa Nov 12 '15 at 10:56
1

This function will take one of said pointers and call func2 if possible, and simply return false otherwise

bool CallFunc2(Base* Bae){
  Derived* Der;
  if (Der = dynamic_cast<Derived*>(Bae))
    {Der->func2(); return true;}
  else
    return false;
}

This works on the principle that dynamic_cast returns a null pointer if the object being cast cannot be converted.

user4578093
  • 231
  • 1
  • 3
  • 10
1

If you don't want to use RTTI at all (including dynamic_cast), you could simulate its behaviour like Qt does it with qgraphicsitem_cast

Outline:

class Base {
public:
    enum { Type = 0 };
    virtual int type() { return Type; }
};
class Derived : public Base {
public:
    enum { Type = 1 };
    int type() { return Type; }
};

template<typename T>
inline T myobject_cast(Base *b) {
    if (b) {
       // Requires C++11
       if (int(std::remove_pointer<T>::type::Type) == b->type()) {
          return static_cast<T>(b);
       }
       /* Pre C++11 (might be UB, but works on many compilers, OpenSource and Commercial)
       if (int(static_cast<T>(0)->Type) == b->type()) {
          return static_cast<T>(b);
       }
       */
    }
    return NULL;
}

// use:
Base *b = new Base;
Base *d = new Derived;
Derived *o1 = myobject_cast<Derived*> (b); // NULL
Derived *o2 = myobject_cast<Derived*> (d); // d

Each class would require a unique Type member for this to work.

Be aware that this will not work with "intermediate" classes in the hierarchy. Only the actual, most derived type will can be cast to (e.g. a DerivedDerived cannot be cast to a Derived, or Base for that matter).

You might also find an overload for const handy:

template<typename T> inline T myobject_cast(const Base *b)
{ return (b && int(static_cast<T>(0)->Type) == b->type()) ? static_cast<T>(p) : 0; }

const Base *cb = new Derived;
const Derived *co = myobject_cast<const Derived *>(cb);
king_nak
  • 11,313
  • 33
  • 58
  • I had this idea to just add a function that gets the type in the super class an override it everywhere. But I wanted something simpler. I think the dynamic cast is the way to go ;).... Thanks :) – mkmostafa Nov 12 '15 at 10:50
  • Use `T::Type` instead of `static_cast(0)->Type` (I'm not sure if your version is UB or not). – Jarod42 Nov 12 '15 at 11:09
  • That won't do, unfortunately. As `T` is `Derived*`, it would generate `Derived*::Type`, which is not correct. Regarding UB, I don't know for sure either. But Qt is used on quite a variety of platforms and supports many compilers, so it is quite safe to use. But alas, not 100% sure... – king_nak Nov 12 '15 at 12:55
  • So `std::remove_pointer_t::Type` to manage pointer correctly. – Jarod42 Nov 12 '15 at 22:03