livefree75
livefree75

Reputation: 763

Ensuring a pointer to a superclass points to an object of a certain subclass in c++

I have two containers (call them type A and type B), both derived from a parent type (call it AB). The parent class has a linked list of Widget objects. In my Widget class, I have a pointer back to the container it's in. Obviously this is declared as a pointer to AB. Also in the widget class, I have a method that ONLY applies if the widget's container is A, and it uses the pointer to AB. But of course, I would need to cast it to an A* .

I've considered making the method a virtual method in AB, and have it throw an error; and then overriding it in A, but this bothers me because the method only applies to A and doesn't make sense in the generic AB case.

I've considered using dynamic_cast to cast the parent pointer to A*. But I've also read that this suggests poor class design. What's the preferred way to handle this?

class Widget;

class AB
{
    private:
        Widget* firstWidget;   // first widget in a linked list, don't judge.
};

class A : public AB
{
    public:
        void methodSpecificToA() {}
};

class B : public AB {}; 

class Widget
{
    private:
        AB* container;
    public:
        void AWidgetMethod()
        {
            A* ac = static_cast<A*>(container);
            ac->methodSpecificToA();
        }

};

Upvotes: 1

Views: 59

Answers (1)

JaMiT
JaMiT

Reputation: 16853

The comments mentioned a desire to not have the base class provide all virtual methods that some derived class might need. This desire is a Good Thing. Let's see if I can lay out some alternatives. (I make no claims about this being exhaustive.) Please recognize that these are generic, as the question involves few specifics about the context. So I'm guessing a bit. I don't know what is best for the given scenario.

Before looking at alternatives, what is wrong with the design? The main problem is that the Widget class knows something about the AB hierarchy, then it makes decisions based upon this knowledge. The less your classes know about each other, the more isolated they are. For people, isolation is bad, but for classes it means less interdependence and fewer bugs introduced when one of the classes changes. Ideally, the Widget class knows the generic AB interface and nothing else in the hierarchy. Maybe a class derived from Widget knows something about a class derived from AB. Or maybe not. There are different approaches that can be taken.

Say what happened, not what to do. One reason this situation might come up is that the widget responds to a certain stimulus by telling the container to do something. Sometimes one should take a look at all the things a container might need to do, and compare that to all the things a container might need to respond to. You might end up with a shorter list of virtual functions if there is one per stimulus rather than one per action taken. Plus, a function for a stimulus should feel applicable to the base class, whereas a function for an action might not. (For example, a function for a stimulus might be WasPressed(Widget & widget).) If you go with this approach, the widget responds to a certain stimulus by telling the container that the stimulus occurred, letting the container decide what it needs to do. In some cases, this by itself reduces the knowledge one class has about another. In other cases, it will seem more contrived (in which case, it's not necessarily a good idea).

Specialized classes deserve specialized data. Well, not always. Still, there are times when it's worth a little extra bookkeeping to keep your code robust in the face of maintenance (a.k.a. other people "improving" your code). I am thinking of the situation where the widget calling methodSpecificToA knows its container is an A because it is a certain type of widget. For example, a scrollbar would know that its parent is a scrollable area because that's the only kind of container that would want a scrollbar. In this case, it may be worth having the specialized widget maintain its own pointer to its container, stored as a pointer to A. (This is in addition to the pointer to AB that you already have.) No casting is necessary, and it may even be possible to separate this special functionality from the concept "my container". (Maybe A should have two base classes: AB and one that defines methodSpecificToA. If you do this, the specialized widget would store a pointer to this new base class.)

Use a callback. This classic method works wonders when it comes to one class not needing to know anything about another class. It works even if the widget is not specialized, but works best if there are not many callbacks needed. (If many callbacks are needed, "specialized data" as above might be better.) With a callback, the widget is told to call a certain function object when a certain stimulus happens, so that's what it does. No knowledge of what's going to happen, and no need for a pointer to its container. The knowledge of how containers and widgets interact shifts from the widget to the code that places widgets in containers. (This is the code that would set up the callback.)


One thing you might notice about these suggestions is that none of them tells you how to call methodSpecificToA from a pointer to AB. That is why I suggest looking deeper into this sort of issue. Once you see a warning flag that you may have a design flaw, it's often a good idea to review your design. The whole design, not just the small part where the flag appeared.

Upvotes: 1

Related Questions