Up/Downcasting pointer in inheritance

Assume I have derived class "Dog" , "Philosopher".
and base class "Speaker".

My professor called the method "void pontificate() const" , which are defined in Philosopher , but I don't get the algorithm that he called. Can someone explain it , please?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Speaker
{   
...
   virtual void speak() const = 0;
   virtual void announce(char *) const = 0;
...
}


class Dog : public Speaker
.
.
class Philosopher : public Speaker
.
.
.
int main ()
{
  Speaker *current;

   current = new Dog;
   current->speak();

   current = new Philosopher("I think, therefore I am.");
   current->speak();


  // how come ??
  // ** (Philosopher *)current) ** ---> what does it mean??
  // why there is nothing follows * sign ??

  ((Philosopher *)current)->pontificate();
   return 0;
}
Last edited on
Sorry I forgot to mention pontificate() method

1
2
3
4
5
6
7
class Philosopher : public Speaker 
{
public:
...
void pontificate() const
...
}
That looks ugly. Could you post the rest of the Speaker code?

Anyway, in C++ there are a number of ways to improve that snippet. First, prefer the C++ cast operator dynamic_cast.

Additionally, casting in general can often be avoided by design improvements. The abstractions being modeled might not quite be right. For example, consider an alternative model using the Bridge/Strategy and Null Object patterns to completely eliminate the need for the cast.

Here's one possible solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* abstract class that models a hierarchy of dialect */
struct Tongue {
    virtual ~Tongue() {}
    virtual void lick()  = 0;
    virtual void speak() = 0;
    virtual void annoy() = 0;
};

/* mute tongue or dialect */
struct NullTongue : public Tongue {
    virtual void lick()  {} // doesn't do anything
    virtual void speak() {} // doesn't do anything
    virtual void annoy() {} // doesn't do anything
};

/* an opinionated tongue or dialect */
struct OpinionatedTongue : public Tongue {
    virtual void lick()  {} // doesn't do anything
    virtual void speak() { std::cout << "This is what I think." << std::endl; }
    virtual void annoy() { std::cout << "Allow me to pontificate!" << std::endl; }
};


/* base class, as before */
struct Speaker {
    // unless otherwise specified, this speaker is mute
    Speaker() : tongue_( new NullTongue() ) {}
    // this is how it can be otherwise specified
    Speaker( Tongue * tongue ) : tongue_( tongue ) {}
    virtual ~Speaker() { delete tongue_; }

    // provide access to tongue or dialect
    Tongue & tongue() { return *tongue_; }

private:
    // has a tongue or dialect*
    Tongue * tongue_;
};

/* derived class */
struct Philosopher : public Speaker {
    Philosopher() : Speaker( new OpinionatedTongue() ) {}
};

* Note that the proper copy and assignment operations were omitted for simplicity. Additionally, a better implementation might use a reference-counted pointer, such as boost::shared_ptr.

With this approach, you can do this:
1
2
3
4
5
6
7
Speaker * a = new Philosopher();
Speaker * b = new Dog(); // Dog was omitted above, as well

// the call to tongue will always return a valid Tongue object and the
// call to annoy will always exhibit the proper behavior
a->tongue().annoy();  
b->tongue().annoy();


As [partially] mentioned above, the advantages are that you do not have to test what kind of Speaker the object is (no casting) and you could even modify this approach to swap out Tongues at runtime.

Note that I literally just spent a few minutes typing this up and have not tested it. I would be surprised if I didn't make at least a few syntactical mistakes.


I got carried away on a bit of a tangent here. Sorry about that. The use of composition for the Tongue hierarchy is not necessary for this example unless you wanted to swap them at runtime. Speaker could inherit from Tongue and implement the default behaviors, which can be overriden in Philosopher.
Last edited on
I've added some more information. The rest of the code is not involved my question about ((Philosopher *)current).
The speak method is implemented with polymorphism, as I am suggesting. The pontificate method is not--it is only available for Philosopher objects. Since current is a pointer to a Speaker, you may only access methods defined in Speaker. Since the real object is a Philosopher (which, is a Speaker), that object has a pontificate method. What is happening is that (Philosopher *)current is a cast to allow you to treat current as a pointer to a Philosopher, and thus call the pontificate method.

It is a C-style cast. In C++, dynamic_cast<Philosopher *>( current ) would do the same, except that there is a runtime check to verify that the object pointed to by current is actually a Philosopher.
current = new Philosopher("I think, therefore I am.");

This call to new actually gives you a Philosopher*. This pointer is being implicitly cast to a Speaker*. This works because all Philosophers are also Speakers (a Philosopher "is a" Speaker).

However that "is a" relationship is a one way street. All Philosophers are Speakers, but not all Speakers are Philosophers. A Speaker could be a Dog, for example.

This code doesn't work current->pontificate(); because current is a Speaker, and all Speakers can't pontificate. Only Philosophers can. The program has no way to know [at compiletime] that current actually points to a Philosopher.

For instance, say you tried the following:

1
2
current = new Dog;
current->pontificate();


That obivously doesn't make sense. That's why if you have a Speaker* you can only do things with it that all Speakers can do. If you need to do something specific to a certain kind of speaker, you can downcast.

(Philosopher*)current is a C-style cast which "converts" current from a Speaker* to a Philosopher*.

1
2
3
4
5
6
// this:
((Philosopher *)current)->pontificate();

// is basically shorthand for this:
Philosopher* p = (Philosopher *)current;
p->pontificate();



Note however that downcasting (and especially C style downcasting) is dangerous, as it makes it possible to do things that cause all sorts of problems.

Take this, for example:

1
2
Speaker* current = new Dog;
((Philosopher *)current)->pontificate();


That obviously makes no sense because Dogs can't pontificate. However the code will compile without error! This leads to very strange, unpredictable, hard to find, and possibly fatal bugs in programs. And in fact, there's almost always an alternative to downcasting, so in most situations where you're doing it, you might want to rethink because there's typically a better/safer way to do what you want.

As mentioned, a safer way to downcast is to use dynamic_cast. dynamic_cast will actually confirm to make sure the Speaker actually is a Philosopher before it actually downcasts. Ensuring that code you write will be safe. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
Philosopher* p = dynamic_cast<Philosopher*>(current);

if(p)
{
  // 'current' really did point to a Philosopher, so we're okay
  p->pontificate();
}
else
{
  // 'current' did not point to a Philosopher.  It must have pointed to something else (like a Dog
  //   or some other kind of Speaker)
  //  so don't use 'p' here
}
Topic archived. No new replies allowed.