Inherit from two polymorphic classes
Asked Answered
Z

3

2

Given the following code

class T {
    public:
        virtual ~T () {}
        virtual void foo () = 0;
};

class U {
    public:
        U() {}
        ~U() {}
        void bar () { std::cout << "bar" << std::endl; }
};

class A : public U, public T {
    public:
        void foo () { std::cout << "foo" << std::endl; }
};

int main () {
    A * a = new A;

    std::vector<U*> u;
    std::vector<T*> t;

    u.push_back(a);

    t.push_back(reinterpret_cast<T*>(u[0]));

    u[0]->bar ();
    t[0]->foo ();

    delete a;
    return 0;
}

I get the output I would expect

bar
foo

However, if I change the definition of U to

class U {
    public:
        U() {}
        virtual ~U() {}
        virtual void bar () { std::cout << "bar" << std::endl; }
};

I still compile fine and without warnings/errors but the output is now

bar
bar

What is it about the virtual declaration that prevents me from calling into the foo?

Zebulon answered 28/1, 2010 at 23:58 Comment(3)
reinterpret_cast is the root of all evil. It was created only so that your team leader knows what to grep for in your code to find your errors ;).Welter
@KornelKisielewicz, That's an awesome quote!Bourassa
you can invoke foo on any pointer to either a T or an A. foo has nothing to do with U. So I don't quite understand your comment about "virtual declarations" in U.Bourassa
P
4

Firstly, there are no virtual base classes in your example. Classes that contain virtual functions are called polymorphic. (There is such thing as "virtual base classes" in C++ but it has nothing to do with your example.)

Secondly, the behavior of your code does not depend on any virtual declarations. You have deliberately destroyed the integrity of the base pointer by using reinterpret_cast. For this reason the behavior of the code is undefined.

A direct cast from one base pointer to another (which is what you are trying to do in your code) is called cross-cast. The only cast in C++ that can carry out a cross-cast is dynamic_cast.

t.push_back(dynamic_cast<T *>(u[0])); 

You can perform an indirect cross-cast without dynamic_cast, but for that you have to downcast the pointer to the derived type first (A *) using static_cast and then upconvert it to another base pointer type

t.push_back(static_cast<A *>(u[0])); // upconversion to `T *` is implicit
Peridium answered 29/1, 2010 at 0:5 Comment(1)
Updated topic to reflect the polymorphic comment.Zebulon
W
1

If you use reinterpret_cast you loose all guarantees, and anything you do is "undefined behaviour". In this case, I expect the VMT got messed up, or the VPTR overwritten.

As an illustration, when I compile the first code above, I get a segfault on execution on my compiler.

If you really want to "cross-execute" you should derive from a common base class, and inherit that base class by U and T virtually ( : virtual public), or use dynamic_cast instead of reinterpret_cast.

Welter answered 29/1, 2010 at 0:4 Comment(2)
Thanks. I didnt know about virtual publicZebulon
@Zebulon - the difference when you inherit virtually, you inherit the same base class, hence forming a <> and not a \/ when having a common base classWelter
B
0

Populate t just like you did u:

t.push_back(a);

You don't need reinterpret_cast because A is a T.

Bourassa answered 29/1, 2010 at 0:5 Comment(4)
I think he explicitly didn't want that.Welter
In my particular case I dont have access to the original object pushed into the vector, I've only got the object in the vector itself. It's the reason I'm trying to do all that.Zebulon
@ezpz, use dynamic_cast then, remember however that it can return NULL.Welter
@KornelKisielewicz, I didn't understand what he was trying to avoid. With that in mind, I think AndreyT is closest to the answer he probably wants.Bourassa

© 2022 - 2024 — McMap. All rights reserved.