开发者

C++ Abstract Base Class Problem

So I have run into a problem with my ABC design in C++. I'm going to use a simplified example from what I actually have. Please ignore any syntax errors until I get to where my issue is.

So I have an ABC:

class ABC  
{  
public:  
    virtual void DoSomething() = 0;  
};

Then I have a derived class:

class Derived: public ABC  
{  
public:  
    void DoSomething() { // something }  
};

In my main logic function I have something along the lines of:

ABC* obj = new Derived;
obj->DoSomething();

Now at this point my code works beautifully, I have multiple classes functioning correctly based on if I change the type (manually for now) from one derived class to another using the abstract base class as the type for the derived objects.

Now my issue...

If I want to change my derived class to add in functionality that is not supported by the ABC then my compiler is not recognizing them whatsoever. For example if I leave ABC the way it is and change Derived to:

class Derived: public ABC  
{  
public:  
    void DoSomething() { // something }  
    void DoSomethingElse() { // something else, not defined in ABC}  
}; 

Then back to my main logic function:

A开发者_开发技巧BC* obj = new Derived;  
obj->DoSomething(); 
// Compiler does not recognize this, due to not being in the "ABC" class   
obj->DoSomethingElse(); 

My compiler keeps giving me errors that "DoSomethingElse()" is not a member of the ABC. Is what I am trying to do possible? It feels like I am overlooking something simple but I've hit this road block in my architecture of this piece of software. Any help is greatly appreciated.


You are trying to invoke a method which only exists in the derived class, so you would need to cast the obj pointer like so to invoke this method:

Derived* derivedObj = dynamic_cast<Derived*>(obj);
derivedObj->DoSomethingElse();

Note that dynamic_cast will do a run-time check to ensure that obj is part of the same class hierarchy (i.e. it is a "valid" cast), and will return 0 if the cast is invalid. Note that you need to enable RTTI to use dynamic_cast.

Casting like this can be considered a code-smell, and there may be a better way to achieve what you are doing.

Polymorphism (what you are trying to achieve here) works better when you are using abstract base classes - i.e. when the methods are pure virtual in the base class, and implemented in any derived classes. This is analagous to interfaces in other languages.


This is completely correct, the obj pointer you are using is of the ABC type, so according to the principle of Polymorphism, it will only have the methods available to the ABC class. This is, of course, completely correct and sensible if you think about it.

If you really need access to the special methods on the derived objects, you need a pointer of that type, as it wouldn't really BE that type until you do that.


In my opinion you have a broken design. What you want to do can't be done.

Think about this... How does code that calls the function that returns the DirextX9 device type know that that function exists? What happens if it gets an ABC * that points at an instance of the DirectX10 functionality?

In my opinion, what you should do is figure out the major abstractions that exist in all versions of DirectX (which, being a Unix geek is an API with which I am wholly unfamiliar and think you ought to be using the cross-platform OpenGL instead). Then you create an interface for each abstraction and make it into an ABC. The interfaces for each ABC only mention other ABCs. Then you derive a specific class for each abstraction that implements it in terms of the types and functions of a specific version of the DirectX API.

Now, you might end up with something like this:

class Sprocket;

class Widget {
 public:
    virtual void squeak() = 0;
    virtual bool nudgeSprocket(Sprocket *sp);
};

class Sprocket {
 public:
    virtual void turn() = 0;
};

class GoldWidget {
 public:
    void squeak();
    bool nudgeSprocket(Sprocket *sp);
};

class GoldSprocket {
 public:
    void turn();
    virtual int glitter();
};

class SilverWidget {
 public:
    void squeak();
    bool nudgeSprocket(Sprocket *sp);
};

class SilverSprocket {
 public:
    void turn();
    virtual int shine();
};

Now, it may be that you only have machines made out of gold or silver but not both. And you might decide that GoldWidget::nudgeSprocket(Sprocket *sp) needs to be able to call GoldWidget::glitter() on its sp argument, and that this will be perfectly safe. In my (and may others) opinion, this is a design error.

One of two things should be true. Either the nudgeSprocket method should be implemented in terms of the generic Sprocket interface, or there should be a class that knows it is dealing with a GoldWidget and will call a version of nudgeSprocket that takes a GoldSprocket as an argument.

I would suggest reading about design patterns as I think knowing about this particular way of thinking about things will help you organize your thoughts about how to do this and result in a relatively clean design. In particular, I suspect the Adapter pattern, the Bridge pattern, and the Facade pattern used in some combination might work well for helping you solve your problem.


DoSomethingElse is a member of Derived, not ABC, so the compiler can't assume you can call it on a pointer of type ABC. You could upcast to a Derived*, or change the ABC* to a Derived* since you know it's derived, or you could move DoSomethingElse to the Abstract class.


Because base class object can't refer to something which is not part of it.


It sounds like you want to use RTTI (http://en.wikipedia.org/wiki/Run-time_type_information) - if the pointer happens to be a SubClassFoo, you have additional functionality you can use. If it doesn't, you might be out of luck on special functionality, but might be able to degrade gracefully.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜