1

Hi i have a question regarding how to access parts of inherited code.

Say i have this WorldObject that is a base class for alot of other objects. Then i have a class Chest that inherit from WorldObject and also from the abstract class OpenAble, with some methods like open and unlock.

In my main i have a vector of WorldObjects that i iterate through with a for loop. Now to the question, how can i check if a worldobject is also of OpenAble and how can i access the methods in OpenAble.

class WorldObject
{
...     //implementation
};

class OpenAble
{
public:
    OpenAble(){}
    virtual ~OpenAble(){}
    virtual void Open() = 0;
    virtual void Unlock(int k) = 0;
};

class Chest : public WorldObject, public OpenAble
{
...  //implementation
};

main()
{
std::vector<WorldObject> objVector;     //vector with several Worldobjects

for (int i =0; i < objVector.Size(); i++)
{
//check if a WorldObject is also of openable
//Do som actions like, open or unlock
//How?
}
};
Holmis
  • 13
  • 3
  • You can only work with polymorphism when using a container of (smart) pointers. Then dynamic_cast can be used, even though I would have questions about the design when dynamic_cast is over used. – stefaanv Mar 11 '14 at 10:09

3 Answers3

1

You could do a dynamic_cast<OpenAble>. This will throw an error if it is the wrong type though which is relatively expensive given that it is quite likely that the object will be the wrong type.

try{
  OpenAble& opener = dynamic_cast<OpenAble&>(worldObj);
} catch (std::bad_cast& ex){
  //not openable
}

BTW: As pointed out in the comments below, if you use a pointer to the base class in your container instead of references, then you can (and should) use the pointer version of dynamic_cast which will return a null in the case that your object is not OpenAble. Checking that in your case would be a lot more efficient than throwing and catching exceptions.

I would recommend an entirely different approach though. Inject your base class with an "OpenPolicy".

E.g.

class CanOpenPolicy {
public:
  boolean canOpen(){ return true; };
  boolean canClose(){ return true; };
  boolean isOpen(){ return openState; };
  void open(){ openState = OPEN; };
  void close(){ openState = CLOSED; };
}

class NoOpenPolicy {
public:
  boolean canOpen(){ return false; };
  boolean canClose(){ return false; };
  boolean isOpen(){ return CLOSED; };
  void open(){ throw IllegalWorldObjectAction("OpenPolicy disallows operation"); };
  void close(){ throw IllegalWorldObjectAction("OpenPolicy disallows operation"); };
}

//injection via template (no need for base "OpenPolicy" class, maybe some
// obscure error codes at compile though)
// Implicit interface based on how you use the injected policy.
template<OpenPol>
class WorldObject {
private: 
  // CTOR part of the injected contract so you are not tied to knowing how to 
  // build the policy. This is a key benefit over interface based injection.
  OpenPol openPol; 
  ...
public:
  ...
  void open(){
    if(openPol.canOpen()){
      openPol.open();
    }
  }
  ...
}

That's not tested or anything. Just to illustrate the idea. You can add multiple policies for different possible operations and the best thing is that you won't need a lot of hierarchies.

To use it just do something like this:

std::unique_ptr<WorldObject>( new Chest() );
std::unique_ptr<WorldObject>( new Banana() );
std::unique_ptr<WorldObject>( new Chair() );

where:

class Chest : public WorldObject<CanOpenPolicy> {
   // Very little implementation in here.
   // Most of it is handled in the base class and the injected policies :)
}
class Banana: public WorldObject<CanOpenPolicy> {
}
class Chair : public WorldObject<NoOpenPolicy> {
}
Dennis
  • 3,683
  • 1
  • 21
  • 43
  • 1
    Just a note, dynamic_cast requires RTTI; see also [this question](http://stackoverflow.com/questions/2253168/dynamic-cast-and-static-cast-in-c) and the accepted answer. – Kristian Duske Mar 11 '14 at 10:06
  • 1
    @Dennis, Should it not be `dynamic_cast<>OpenAble&>(...)` ? – utnapistim Mar 11 '14 at 10:09
  • 1
    The cast would have to be to a reference or pointer. (A pointer cast, with a check for null to see if it succeeded, might be better than a locally-handled exception, in terms of both efficiency and general flappiness.) – Mike Seymour Mar 11 '14 at 10:09
  • Yeah would be better to use pointers and check, but OP was using references so I thought I should do too. Anyways my real answer is to not use polymorphism like this. – Dennis Mar 11 '14 at 10:14
  • @utnapistim - Sorry yes. Corrected. – Dennis Mar 11 '14 at 10:14
0

The most important thing, even though you may not like this, is to not throw away type information in the first place.

Collections of generic "object" is a Java'ism, it's not how to do things in C++.

That said, provided the statically known class is polymorphic (has at least one virtual member function), you can use dynamic_cast or typeid. This functionality is known as RTTI, short for Run Time Type Information. With some compilers you have to use special options to enable RTTI.

Idiomatic use of dynamic_cast:

WorldObject* p = ...;
if( auto p_openable = dynamic_cast<OpenAble*>( p ) )
{
    // use p_openable
}

Note that dynamic_cast to pointer signals failure by returning a nullpointer, while dynamic_cast to reference signals failure by throwing an exception, since there are no nullreferences.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @stefaanv: "software object" isn't a meaningful term in the context of programming. – Cheers and hth. - Alf Mar 11 '14 at 10:15
  • @stefaanv: maybe i'm a dullard, but what's an unreal game object, or for that matter, a game object? – Cheers and hth. - Alf Mar 11 '14 at 10:19
  • @Cheersandhth.-Alf I think stefaanv means that, for example, the amount of light in a room might be implemented as a class, which could be created as an object on a room by room basis, but that 'light' wouldn't be something you directly interact with etc (beyond the obvious - you see using it) – chrisb2244 Mar 11 '14 at 10:23
  • i'm not going to answer the nonsense comments above, except to note that the last one falsely attributes a nonsense view to me, before going on with more of the author's nonsense. – Cheers and hth. - Alf Mar 11 '14 at 11:00
0

The simple (obvious) solution is to use dynamic_cast and cast your objects to OpenAble.

The problem with "the simple (obvious) solution" is that usually, use of dynamic_cast shows a lack of flexibility in your class hierarchy and is a symptom of a design problem.

I would offer the OpenAble interface as a set of behavior exposed through a handle:

class OpenAble { /* ... */ };

class WorldObject
{
    //implementation
    virtual OpenAble* GetOpener() { return nullptr; }
};

class Chest: public WorldObject {
    struct ChestOpener: public OpenAble {
         Chest *c;
         virtual void Open() {
             // do stuff with c
         }
    };
    std::unique_ptr<OpenAble> chest_opener;
public:
    virtual OpenAble* GetOpener() {
        if(!chest_opener) {
             chest_opener = new ChestOpener{ this };
        }
        return chest_opener.get();
    }
};

Client code:

std::vector<WorldObject> objVector;     //vector with several Worldobjects

for(auto &obj: objVector)
{
    if(auto openerHandle = obj.GetOpener())
        openerHandle->Open();
}
utnapistim
  • 26,809
  • 3
  • 46
  • 82