Overriding methods and virtual pointers

Started by
8 comments, last by SiCrane 14 years, 10 months ago
In the GUI system i wrote for my game i have a Component class (C++) which represents any GUI component. I also have a Container class which is a subclass of Component, it holds other components and is used for windows, panels and scrollable containers. I also have a stack of visible components/containers (i asked about using stacks the other day. fyi, i went with a vector). The top of the stack is the active component, the one that has focus. This can be any component (useful for the drop down box of a combo box) but usually is a Container. In my event code i check which component is under the mouse by drilling down recursively through containers and iterating over their contents checking if each one is in bounds:

void Event::notifyMousePressed(MouseEvent* e) {
    Component* cmp = Component::getActive();
    if (cmp) {
        mouseFocus = cmp->getComponentAt(e->getX(), e->getY());
        if (mouseFocus) mouseFocus->mousePressed(e);
    }
}



Previously i cast to Container but i realised that i don't need to since any Component can have focus. Also, getComponentAt is implemented in Component and overridden in Container:

/// Component.cpp //////////////////////////

/* If x, y is within this component then return it, otherwise NULL  */
Component* Component::getComponentAt(int x, int y) {
    return (inBounds(x, y) ? this : NULL);
}

/// Container.cpp //////////////////////////

/* Drill down through this container's children until we have found */
/* one which is at the x, y coords. If there is no children then    */
/* this is returned, if this is not at x, y then NULL is returned.  */
Component* Container::getComponentAt(int x, int y) {
    Component* child = NULL;
    if (inBounds(x, y)) {
        for (unsigned int i = 0; i < components.size(); i++) {
            child = components->getComponentAt(x, y);
            if (child) return child;
        }
        return this;
    }
    return NULL;
}



But when i changed it, no events worked. I stepped through using the Visual Studio's debugger and it seems to be calling Component::getComponentAt even for a Container object. Is this right? Shouldn't it be calling the overridden method in Container, or do i have to do something with virtual? btw, this may sound n00bish but i still don't know the difference between virtual void somthing(); and virtual void somthing() = 0;
[Window Detective] - Windows UI spy utility for programmers
Advertisement
If you have a pointer or reference to the type of the base class object and you want the derived class version of the function to be called, the function must be virtual. Of course, the obvious question is, since you already guess that virtual might have something to do the answer is why you didn't try it before posting.
Quote:Original post by XTAL256

btw, this may sound n00bish but i still don't know the difference between
virtual void somthing();
and
virtual void somthing() = 0;


virtual void somthing();
says you have declared a virtual method. You're telling the compiler that class whatever has an overridable something method and you'll implement it... somewhere.

virtual void somthing() = 0;
says you have declared a virtual method and you're not going to implement it, at least not in it's declaring class.

Basically
virtual void somthing();
means "I've got this method here, you can use it if you want or you can provide your own implementation if you prefer."

virtual void somthing() = 0;
means "I expect you, mr. implementer of derived classes to provide me with an implementation of this method."
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Quote:Original post by SiCrane
Of course, the obvious question is, since you already guess that virtual might have something to do the answer is why you didn't try it before posting.

Because, as i said at the end of the post, i don't know the difference between the two virtual declarations [grin]. Also, i am at work now so i can't try it yet.
thanks guys
[Window Detective] - Windows UI spy utility for programmers
virtual is 7 characters. = 0 is another three. Your original post is something like 2000, depending on how you count the white space. That means that trying to put the keyword virtual in the function declaration, with or without the = 0, is something like two orders of magnitude less work than the post you made. I can understand making a post after you've tried it if you wondered why or how it worked or didn't work. Making the post without having tried it is just silly.
Quote:Original post by SiCrane
virtual is 7 characters. = 0 is another three. Your original post is something like 2000, depending on how you count the white space. That means that trying to put the keyword virtual in the function declaration, with or without the = 0, is something like two orders of magnitude less work than the post you made. I can understand making a post after you've tried it if you wondered why or how it worked or didn't work. Making the post without having tried it is just silly.

Ok, ok, calm down. I probably should have made a bit more effort to figure it out myself but as i already mentioned i am at work now so i can't try it. I wasn't sure if virtual had anything to do with it and since i didn't really get the difference between adding the "= 0" bit i though you guys could clarify.
And it's not two orders of magnitude less work to try it since i still have to compile and run it [grin].
[Window Detective] - Windows UI spy utility for programmers
ChaosEngine, that is close.

For those who are not interested in esoteric properties of C++, read ChaosEngine's post, and believe it. For those that are...

virtual void foo() = 0;
does exactly three things.
First, it says "you cannot create an instance of this class".
Second, it says "if you inherit from this class, and do not override foo, you cannot create an instance of that class either".
Finally, it says "it is allowed for there to be no implementation of foo() in this class".

It does not, however, _prevent_ you from making an implementation of foo for this class.

This allows you to create a default/helper method that children must explicitly say they are using, while still demanding that the children explicitly override your foo().

I've seen about 2 or 3 good uses for this.

First:
class bar {	virtual void foo() = 0;};void bar::foo() {	this->foo();}

allows for some syntactic sugar of:
bar* meh;meh->bar::foo(); 

which would otherwise segfault. I've seen corner cases where this helps.

The above is questionable, because it is a bit too much of a language-morph.

Second, as a form of ghetto "overrides" keyword. In C++, a child class does not distinguish between a method that overrides a virtual method from the parent, and new methods. By making the parent class methods virtual = 0, and requiring that child class methods who want the default behaviour explicitly call the parent class, changes in the parent class interface that are not followed by changes in child class interfaces result in compile-time errors.

In a language that supports keywords like "initial" and "overrides" and the like, this isn't needed. C++ lacks them.

Both of these are edge cases, and probably not worth the strange looks you'll get from the 99% of the C++ population that doesn't know you can implement a pure virtual method. Ie, even though they solve problems, they probably create more problems than they solve.
Quote:Original post by NotAYakk
This allows you to create a default/helper method that children must explicitly say they are using, while still demanding that the children explicitly override your foo().


If every derived class is expected to use the helper, however, this becomes an anti-pattern. Instead, make the "default" a nonvirtual in the base, and have it call to a private, pure virtual "do class-specific work" function.
Quote:Original post by Zahlman
Quote:Original post by NotAYakk
This allows you to create a default/helper method that children must explicitly say they are using, while still demanding that the children explicitly override your foo().


If every derived class is expected to use the helper, however, this becomes an anti-pattern. Instead, make the "default" a nonvirtual in the base, and have it call to a private, pure virtual "do class-specific work" function.

The goal is to make sure that, upon interface changes, that child methods that no longer properly override the parent method ... fail to compile.

Ie, it is a hack-attempt to get around the lack of the 'overrides' keyword in C++ -- C++ does not distinguish between a new function, and an override of a parent function, at the point where you do the action. An interface change in the parent function results in the child 'override' being orphaned.

It isn't that great a work-around. You could split it into three:
class Base {  void default_work();  virtual void actual_work() = 0;public:  void request_work() { actual_work(); }};

where the interface is request_work(), the child _must_ override actual_work() (even with an empty method -- or one that just calls default_work()), and if they want the default "helper" work to be done, they call default_work() (or not, as the case may be).

The single-method version of this is:
class Base {public:  virtual void request_work() = 0;};// ...void Base::request_work() {  // default work goes here}

in which the child class is required to override request_work(), possibly with a method that just calls Base::request_work() to get default behaviour.

Naturally, the only real virtues here are the ability to
A> Avoid the default work at the client object's request, and
B> Force the child to explicitly override each method, to avoid interface changes causing child overrides to become orphan methods.

I could see something along the lines of:
class Base {public:  virtual void request_work() = 0;};class Base_default_request_work: public virtual Base {  void request_work() { ... }};

where you can via inheritance and a mix-in class 'plug in' default behaviour for a given method, without polluting the base class. Sadly, this is also getting stupid in it's complexity.

(Note that this was a real world concern: some colleagues had an interface that was in rapid flux, and multiple child implementations, and the interface often published (optional) default implementations of some methods. And multiple bugs/unit test failures that where tracked down to interface changes that where not perfectly mirrored in child implementation interface changes.)
Quote:Original post by NotAYakk
Finally, it says "it is allowed for there to be no implementation of foo() in this class".

Except for destructors, where the implementation is still required.

This topic is closed to new replies.

Advertisement