Thread: C++ equivalent of super

  1. #1
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591

    C++ equivalent of super

    This has probably been asked and answered a bazillion times... but what is the C++ equivalent of a Java-style "super" reference?

    Google tells me, there can be no such keyword due to multiple inheritance. I'm still a bit confuzzled as to why Base::func() works, as I thought unless func was static, it would need to be called from an object reference (i.e. base.func() ) (special case for virtual functions perhaps?).

    Anyways, I'd like to avoid the above method, as it makes the code vulnerable to change (and reduces its re-usability). Is there any other "method" I can employ, I'm willing to consider macros and/or some minor "hacks"...

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Yes, multiple inheritance means there may not be one parent, but multiple. And how would you know which to use?

    All the functions of the base class are inherited by the derived class along with everything else, so func() is a perfectly ordinary member of the derived class (except for private functions in public inheritance and so on). Of course, if it is overridden, then the derived one will be called instead.

  3. #3
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    I'm still a bit puzzled over the semantics that allow me to make a static call to non-static member Base::foo()... is this just a special case with "virtual" functions?

    Also, what is the best way to write re-usable class code? (i.e. best method of referring to a base class without explicitly calling it by name?)

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Let's look at this:
    Code:
    class A {
        public:
        int bob(int x);
    }
    
    class B : public class A {
        public:
        int tom(int x);
        int george(int x) {return tom(x)+bob(x);}
    }
    I'm assuming this is the sort of thing you mean? (You can call it A::bob(x) if it makes you feel better.) Class B has inherited bob from A, so it is just the same as tom -- it's a member function of the class.

    And I'm not an OOP expert, but I would think if you need to refer to a base class method explicitly, you've messed up your classes.

  5. #5
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    I would think it's a common OO paradigm, i.e "draw".
    Code:
    class Polygon
    {
    public:
        virtual void draw() { // do generic stuff }
    protected:
        long double x;
        long double y;
    };
    
    class Rectangle : public Polygon
    {
    public:
        virtual void draw() {
             // do generic stuff first
             Polygon::draw();
            // now do specific stuff
             }
    
    protected:
        long double width;
        long double height;
    };
    So this first puzzling bit is that Polygon::draw() is not a static member function, yet I can still access it as "Polygon::draw()".
    The second question would be how do I call the Base class's function without explicitly specifying the base class name (i.e. a dynamic reference, something to emulate "super")?

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    The second question would be how do I call the Base class's function without explicitly specifying the base class name (i.e. a dynamic reference, something to emulate "super")?
    Since you have "generic stuff" to do, the solution is to provide a protected virtual function that subclasses override if they want to do more than the "generic stuff", and then have the base class provide the "generic stuff" in a non-virtual function, e.g.,
    Code:
    class Polygon
    {
    public:
        void draw()
        {
            // do generic stuff
            // ...
    
            // do specific stuff, if any.
            drawMore();
        }
    
        virtual ~Polygon() {}
    protected:
        long double x;
        long double y;
    
        virtual void drawMore() = 0;
    };
    
    class Rectangle : public Polygon
    {
    protected:
        virtual void drawMore()
        {
            // do specific stuff
            // ...
        }
    
    protected:
        long double width;
        long double height;
    };
    Last edited by laserlight; 10-19-2008 at 08:56 PM. Reason: Polygon::drawMore() should be pure anyway, since Polygon should be an abstract base class.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Yes. The Rectangle class has inherited the draw function from Polygon. It's a member function, so it's called as such. That's exactly the example I posted, except here Polygon::draw is needed to differentiate it from Rectangle::draw, which would be found first by name lookup.

    Well, maybe you're right that you should call base functions. However, the class needs to know the base class name in order to derive (i.e., if you decide to not inherit from Polygon, but from PolygonV2, you'll have to change the class anyway), so that may be part of the deal there.

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    using the class::member syntax is not calling it as static. it is nothing more than explicitly telling the compiler in which scope you want it to find the function draw(). you have to use that syntax when calling a static function for the same reason - so that the compiler knows where to find it. if you just called draw() in that case, it would probably try to re-enter the function you're already in, leading to major problems. saying Polygon::draw() tells the compiler that you want to call the member function draw() in the parent class Polygon. you have to refer to the parent class by name, rather than as 'parent' or 'base' as in some other languages.

  9. #9
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    @laserlight:
    That's an off-by-one solution though. If I have N derived classes I will need n-1 "drawMores" (i.e. drawMoreMore, drawMoreMoreMore). Not very OO-ish...

    @elkvis, tabstop
    If I've now inherited and overridden draw() from Polygon in Rectangle, won't the call to Polygon::draw resolve to Rectangle::draw? or is some sort of inlining performed?

    So there's absolutely no way to reference the base class by anything else other than its explicit class name? I do understand that I'll have to change the class anyways, *but* that's only in one location and its easy to manage, versus changing many calls to member functions.

  10. #10
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by @nthony View Post
    I'm still a bit puzzled over the semantics that allow me to make a static call to non-static member Base::foo()... is this just a special case with "virtual" functions?
    No.

    You're presumably referring to being able to do;
    Code:
    Derived::something()
    {
         Base::foo();
    }
    If Base::foo() is non-static, then the requirements for this to work are;
    1) Derived to be derived from Base,
    2) Base::foo() to be accessible to Derived (i.e. not private to Base);
    3) The call Base::foo() is unambiguous (ambiguity can result in multiple-inheritence situations).
    4) Derived::something() is a non-static member of Derived.
    5) Base::foo() is defined (i.e. implemented).

    There is no requirement on either function to be virtual.

    Your Polygon::draw() and Rectangle::draw() - in a later post - meet these requirements.

    Quote Originally Posted by @nthony View Post
    Also, what is the best way to write re-usable class code? (i.e. best method of referring to a base class without explicitly calling it by name?)
    Rewriting reusable class code does not require being able to refer to a base class without explicitly calling it by name. It requires design of your class so it can be reused rationally.

    The closest to what you want would be to use a typedef.
    Code:
    class A
    {
        public:
            virtual void foo() {};
    };
    
    class B : public A
    {
         public:
    
             typedef A super;
             void foo() {super::foo();};
    };
    However, this only works in a single-inheritance situation (or in a multiple inheritance situation if A is a virtual base). It can also be confused if the inheritance hierarchy is more than one-deep.

    Generally, I consider this to be extremely poor form (and, yes, I consider that Java should not have supported such a feature). If you are designing class B, one of the design decisions is that A is its base. So it is just as easy to call A::foo() as super::foo().

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by @nthony View Post

    @elkvis, tabstop
    If I've now inherited and overridden draw() from Polygon in Rectangle, won't the call to Polygon::draw resolve to Rectangle::draw?
    Since the two functions are not related to each other, I can't imagine why it would.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by @nthony
    That's an off-by-one solution though. If I have N derived classes I will need n-1 "drawMores" (i.e. drawMoreMore, drawMoreMoreMore). Not very OO-ish...
    Yes, it does mean that if your derived class might have its own derived classes, then it will either have to provide yet another virtual function for its derived class to hook, or you end up with what you have now: the most derived class' override of drawMore() will have to call its base class' drawMore().

    The "OO" aspect lies in the protected virtual function. This is known as the "non-virtual interface" idiom, and can be considered an instance of the template method pattern, in my opinion.

    Quote Originally Posted by @nthony
    If I've now inherited and overridden draw() from Polygon in Rectangle, won't the call to Polygon::draw resolve to Rectangle::draw? or is some sort of inlining performed?
    No, since you have explicitly stated that you want Polygon::draw(), not Rectangle::draw().
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    hmm, yeah that makes sense... I guess I was expecting it to behave as if
    ((Polygon)Rectangle).draw() were called.

    @grumpy
    I meant this by reusable/"resistant to change":
    Code:
    public class Foo extends Bar
    {
        public void draw() { super.draw(); }
    }
    I can now change Bar freely (say to BarV2) without worry of having to change all instances of super.

    But I've figured out a close approximation using a simple macro:
    Code:
    #define SUPER base
    class Foo : public base {
    ..
    }
    undef SUPER
    Yes, it does mean that if your derived class might have its own derived classes, then it will either have to provide yet another virtual function for its derived class to hook, or you end up with what you have now: the most derived class' override of drawMore() will have to call its base class' drawMore().
    I agree providing hooks is more to do with creating interfaces (as is templating); however I'd tend to think that interfacing and derivation are separate concepts in OO design. Meaning one shouldn't have to interface to derive. (or rather, how would you derive from an interface in such a model?)
    Last edited by @nthony; 10-19-2008 at 09:27 PM.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by @nthony
    I can now change Bar freely (say to BarV2) without worry of having to change all instances of super.
    That is true, but you still cannot change Bar freely: any client of your class that uses Bar (e.g., it makes use of polymorphism) must also change. In other words, by changing the base class (or Java interface construct) name, you change (or should I say break?) the interface.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    Sorry, by "change Bar", I meant "change Foo's parent class from Bar to a new class, BarV2, such that Bar remains unchanged". Such a change won't affect clients using Bar (who will continue to use the unchanged Bar) nor clients using Foo if the public interface to Foo remains unchanged.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Windows equivalent to linux curl command?
    By elmutt in forum Windows Programming
    Replies: 5
    Last Post: 02-29-2008, 06:16 AM
  2. Pointer equivalent to array notation
    By bekkilyn in forum C Programming
    Replies: 4
    Last Post: 12-06-2006, 08:22 PM
  3. Replies: 10
    Last Post: 08-17-2005, 11:17 PM
  4. Header File Question(s)
    By AQWst in forum C++ Programming
    Replies: 10
    Last Post: 12-23-2004, 11:31 PM
  5. With super powers would you:
    By Jeremy G in forum A Brief History of Cprogramming.com
    Replies: 12
    Last Post: 09-17-2003, 11:27 PM