Shop OBEX P1 Docs P2 Docs Learn Events
C++ Polymorphism Question — Parallax Forums

C++ Polymorphism Question

DavidZemonDavidZemon Posts: 2,973
edited 2014-06-09 11:23 in Propeller 1
Given that -fno-rtti stands for "Force no runtime type information", I have a question regarding polymorphism when used (compiled) with this option.
#include <stdio.h>

class Parent {
    public:
        virtual void foo () {
            printf("Parent called!\n");
        }


        int x;
};


class Child : public Parent {
    public:
        void foo () {
            this->Parent::foo();
            printf("Child called!\n");
        }
};


void myFunc (Parent &instance) {
    instance.foo();
};


int main () {
    Child instance;
    myFunc(instance);


    return 0;
}

I have code like this and it compiles. It's broken at the moment... so I'm not sure if it works... but it compiles. I would think that, if the program were incapable of determining the type of `instance` within myFunc(), then it wouldn't compile because it doesn't know which version of foo() to call. And if it simply always calls Parent::foo() within myFunc(), what is the point of the virtual keyword when compiled with -fno-rtti?

Thanks,
David

EDIT
I compiled this on x86 and it does indeed work - quite magically I'd say. It printed both "Parent called" and "child called". Magic I tell you.

Comments

  • BlackSoldierBBlackSoldierB Posts: 45
    edited 2014-04-24 01:22
    According to the TightMemoryModel page, the dynamic_cast<> and typeid functions are not working correctly when using the "-fno-rtti" flag.

    This code runs fine on my ActivityBot.
  • Heater.Heater. Posts: 21,230
    edited 2014-04-24 03:37
    Magic indeed.

    I was thinking there is something odd about your:
    [COLOR=#333333][FONT=Parallax]    this->Parent::foo();
    [/FONT][/COLOR]
    
    Never seen that done before.

    It works but is more often written as just:
    [COLOR=#333333][FONT=Parallax]    Parent::foo();
    [/FONT][/COLOR]
    
    Which generates exactly the same code.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2014-04-24 04:35
    Oh, yes. It is odd. despite it being more verbose, I much prefer that methods and member variables be prefixed with 'this' because it is so much more clear to me when I'm reading it
  • ersmithersmith Posts: 6,054
    edited 2014-04-24 05:10
    The code you posted works fine for me on the Propeller. As BlackSoldierB mentioned, it's only dynamic casting and type discovery functions that do not work with -fno-rtti. Regular use of virtual functions should be fine -- the virtual function is basically just a function pointer that's filled in by the compiler based on the class of the object.
  • jac_goudsmitjac_goudsmit Posts: 418
    edited 2014-04-24 09:07
    class Child : public Parent {
        public:
            void foo () {
                this->Parent::foo();
                printf("Child called!\n");
            }
    };
    

    I have a funny feeling that Child::foo() needs to be declared virtual to use polymorphism the right way. Otherwise if you declare a Child and call foo(), it will call Child::foo() but if you declare a Parent pointer and assign it to a Child object, it will call Parent::foo() instead of Child::foo() because the vtable of Child will contain a pointer to Parent::foo() even though there is a Child::foo() declared because your Child::foo() is not virtual.
    // So if you replace your main() with this:
    int main()
    {
      Child child;
      Parent &parent = child;
    
      child.foo(); // Calls non-virtual foo in Child class
      parent.foo(); // Calls virtual foo (polymorphism) i.e. Parent::foo() not Child::foo(), unless you make Child::foo() virtual
    }
    

    Unless I'm mistaken, this is true (and won't generate a compiler error) regardless of whether RTTI is enabled.

    ===Jac
  • Heater.Heater. Posts: 21,230
    edited 2014-04-24 14:36
    jac_goudsmit,

    Well, I just compiled the thing using your main() function and the output is the same whether the child::foo is virtual or not. That is this:
    #include <stdio.h>
    
    class Parent {
        public:
            virtual void foo () {
                printf("Parent called!\n");
            }
    };
    
    
    class Child : public Parent {
        public:
            void foo () {
                 Parent::foo();
                printf("Child called!\n");
            }
    };
    
    int main()
    {
        Child child;
        Parent &parent = child;
    
        child.foo();
        parent.foo();
    }
    
    Produces this:
    Parent called!
    Child called!
    Parent called!
    Child called!
    
    Make Child::foo virtual and the result is the same.
  • Heater.Heater. Posts: 21,230
    edited 2014-04-24 14:50
    Whilst we are at it, we can also do this:
        Child* cp = new Child();
        Parent* pp = cp;
    
    
        cp->foo();
        pp->foo();
    
    Again, making Child::foo virtual or not makes no difference.

    I don't think there is a vtable involved in these cases. The compiler can find the correct functions to call at compile time.
  • neildarlowneildarlow Posts: 4
    edited 2014-06-02 13:52
    Hi,

    For polymorphism to work, the polymorphic functions must be declared virtual in all class definitions.

    In a scenario where Child1 and Child2 are derived from the Parent class, and each has a virtual member function foo, calling the foo function using a pointer to, or an instance of, either Child1 or Child2 will call the appropriate class member function foo.

    Where the parent member function foo has no meaningful implementation it is traditionally declared as a pure virtual function - otherwise it is generally invoked by the derived member function to process inherited data members or execute member functions belonging to the Parent class alone.

    The use of this->Parent::foo is, of course, redundant and not good C++ style. The compiler takes care of using this in relation to member function calls.

    Regards,
    Neil Darlow
  • photomankcphotomankc Posts: 943
    edited 2014-06-09 09:36
    Correct me if I am wrong but RTTI is not *required* for basic polymorphic objects to operate. So basically lets say I have this arrangement.


    Vehicle
    - Truck
    - Car
    - Plane
    - Boat


    If I create a Car object and store its address in a Vehicle pointer there's not much magic. The object is a car, however the pointer I use is type Vehicle so the only methods I can use via that pointer are those that work with any Vehicle. So lets say all Vehicles can Move(), Turn(), Start(), Stop(), then you can use all those functions and they will call the appropriate functions in your Car, Boat, or Plane object via the v-table. You can't however tell if any of your vehicles are Planes vs Boats vs Cars. So if you need to call Car::ChangeTire() and all you have is a Vehicle pointer.... you are boned. RTTI lets you figure out that this Vehicle pointer is actually a Car and that you can safely cast it and call Car::ChangeTire(). That's my understanding at least.

    Your code has got to be able to live with the least common denominator aspect of the class hierarchy if you don't use RTTI.
  • jazzedjazzed Posts: 11,803
    edited 2014-06-09 11:23
    photomankc wrote: »
    Your code has got to be able to live with the least common denominator aspect of the class hierarchy if you don't use RTTI.


    Right, a class that wants to inherit from a pure abstract class with virtual functions must implement the virtual functions.

    Polymorphism is achieved with virtual functions. RTTI is not necessary.

    RTTI can be useful, but needing it to choose the right class defeats the idea of polymorphism to a large extent.
Sign In or Register to comment.