Link to home
Start Free TrialLog in
Avatar of Slarti
Slarti

asked on

dynamic cast and static cast

Can anyone explain to me the difference between dynamic casts and static casts? For instance, I'd like to see an example in which static casts wouldn't work.
-- Slarti
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nietod
nietod

For example, in the class heirarchy

class Base
{
public:
   virtual ~Base() {};
};

class Derived1 : public Base
{
public:
   int a;
};
class Derived2 : public Base {};

If there is a function that is passeed a pointer to a base class, like

void Function(Base *BPtr)

The function can be passed a pointer to an object this derived from Base, like a Derived1 or Derived2.  If the function wants to downcast the pointer so that it points to a Derived1 object, like if it wants to access the "a" member of the class, then it needs to down-cast the BPtr parameter with a dynamic cast.   If it uses a static cast, like

Derived1 *DPtr = (Derived *) BPtr;

the cast will always convert the specified pointer to a pointer to a Derived1 object.  However, that is not a good thing.  If the object that is pointed to is really a Base object or a Derived2 object, then this conversion is a mistake.  Trying to use one of these objects like a Derived1 object (like trying to access the "a" member) will cause problems and maybe even crash the program.

However, a dynamic cast will look at the information at run-time and will  determine if the conversion is possible, if so it will do it.  If not, it will return NULL.  So a cast like

Derived *DPtr = dynamic_cast<Derived *>(BPtr);

will either set DPtr to a non-NULL pointer that is safe to use or to NULL.
Dynamic_cast  is also needed for cases where there is  a virtual base classe, because conversions to or from a virtual base class may actually involve a change in the pointer's value (not just its type) that must be determined at run-time.  (i.e the virtual base may be at an address that cannot be determined from the derived class at compile-time or vice versa.)
Avatar of Slarti

ASKER

nietod, I understood everything up to the last comment. I am not sure I know what a virtual base class is. Can you explain this part in more detail? Thanks. So far your answer has been excellent.
Something I forgot to mention is that dynamic_cast is guaranteed to work only an object that has at least one virtual function (whic can be defined in a base class).  That is why I included a virtual destructor in the example.  Otherwise the dunami_cast would be performed at run-time and always produce the same results as a static cast

In a multiple inheritance heirarchy it is possible to have a base class appear in a derived class multiple times, for example.

class Base
{
public:
   int a;
}

class Derived1 : public Base1 {};
class Derived2 : public Base1 {};

class Multiple : public Derived1, Derived2 {};

A graphical depiction of this heirarcy would be

 Base         Base
    |             |
Derived1   Derived2
           \ /
        Multiple.

The Multiple class contains two "Base" objects, one that is the base of the Derived1 and one that is the base of Derived2.  For example the code

Multiple M;

M::Derived1.a = 1;

sets the a member of the "Base" that is the base of the "Derived1" base, but does not affect the base of the "Derived2" base.  So if you then do

cout << M::Derived2.a;

the a member won't have been set to 1.  Sometimes this is desirable, but sometimes not.  Someimes it would be better if there was just one copy of the "Base" base class object in the entire Multiple object.  That is what a virtual base class.  A virtual base class appears only one time in the multiple enheriitance object, for example.

class Base
{
public:
   int a;
}

class Derived1 : public virtual Base1 {};
class Derived2 : public virtual Base1 {};

class Multiple : public Derived1, Derived2 {};

A graphical depiction of this heirarcy would be

          Base      
           / \
Derived1   Derived2
           \ /
        Multiple.

In this case the two "DerivedX" classes would share a single "Base" object as a base class.  For example, when Derived1 made changes to its Base, the change would also occur to Derived2's base.

This sort of design is not used much, as it is often best to avoid mutiple enheritance.  (A bit of a generalization to be sure.)  But it is definitely a useful design at times.  For example the STL uses it in the I/O stream classes.  The input and ouput stream class both enherit virtually from a base class, then the i/o stream class enherits from both the input and ouput stream class.
I wrote this for a related question:


There is no such thing a virtual inheritance.  There is, however, a virtual base class.  I assume that is what you are referring to.  A virtual base class is declared by adding the word "virtual before the class name in the list of base classes, like

class Base
{
};

class Derived : virtual public Base
{
};

Note that the "Base" class is not declared specially.  it is an ordinary class.  But in this case when a class is derived from it is made a virtual base class.  On other class hiearachies it might not be made a virtual base class.  (i.e. whethor or not it is a virutal base class, is dependant on how it is used, it is not enherient in the class itself.)

Now what does it mean?  Well first of all, in a single enheritance class heirarchy it has not effect (no visible effect to the programmer, it may have an effect in how the classes are actually constructed by the compiler and may result in slower performance and more memory use.)  Virtual base classes only have a noticable effect in multiple enheritance class heirarchies.  In this case, if the same class appears as a virtual base class of two or more classes, only a single "instance" of this base class will be stored in the derived class.  

Take the following example:

class Ios
{
public:
   int State; // Current state.
};

class OutStream : public Ios
{
};

class InStream : public Ios
{
};

class IOStream : public InStream, public OutStream
{
};

In this case, which models the STL stream classes.  The input and ouput stream are both derived from a class called Ios then a multiply derived input/output class is derived from both the input and output stream classes.  The problem with this since both the input stream and the output stream classes derived from the Ios class, now there is two  seperate Ios objects "inside" the IOStream class.  Each of these objects have seperate data members, like the "State" member.  Thus the IOStream object might develope ambiguities from this.  For example, if data is read from the object using its InStream base class, it might detect an error and set an error flag in the "State" member of the InStream's base class.  But then before doing some output some code might try to detect the occurance of the error and look at the "State" member of the Ios that is the base of the OutStream class.  This "State" was not set to reflect the error.  See the problem?

Now if instead the Ios class is made a virtual base class of OutStream and InStream, like in

class Ios
{
public:
   int State; // Current state.
};

class OutStream : virtual public Ios
{
};

class InStream : virtual public Ios
{
};

class IOStream : public InStream, public OutStream
{
};

Then there is only one Ios "object" stored inside the IOStream object.  This object is the base of both the InStream and OutStream objects inside the IOStream object.  That means that if InStream changes a data member of its base class, the OutStream class will "see" this change.

Does that make sense?

Now virtual base classes have additional overhead compared to regular base classes.  So you should not use it unless it is actually needed.  Actually, the same could often be said for multiple inheritance.  It should usually be avoided.
Avatar of Slarti

ASKER

nietod, thank you for a clear, detailed explanation.