简体   繁体   中英

Dynamic_cast: should be replaced in this case

There is a base class A, which is virtual

class A
{
  ~virtual A() = 0;
};

and more derived classes B, C, D, E...

class B : public A
{
};

class C: public A
{
};

and analogously for other classed D, E... We have a list of A pointers

std::list <A*> a_list;

We remove any element which type is unknown, for example

A *a = a_list.front();

Base on the type of a pointed object we decide, what to do... There more possibilities how to do that:

A) dynamic_cast case

Recasting of a to derived types.

if (dynamic_cast <B*> (a))
{
   //do something (but nothing with a)
}

else if (dynamic_cast <C*> (a))
{
    //do other (but nothing with a)
}

But the dynamic_cast usage indicates a bad design.

B) Additional attribute

Some aditional attribute, for example an object ID is impelemented;

class A
{
  virtual ~A() = 0;
  virtual short getID() = 0;
};

class B : public A
{
  virtual short getID() {return 1;}
};

class C: public A
{
  virtual short getID() {return 2;}
};

So the modified condition

switch ( a->getID())
{
   case 1: // do something (but nothing with a)
   case 2: // do other (but nothing with a)
}

A note:

We do not perform any action directly with the object, but on the basis of its type we do some different computations.

Questions:

1) Is it the case, when we should avoid dynamic_cast?

2) is there any prefereable solution (may be different to presented)?

Thanks for your help.

According to Item 90 in C++ Coding Standards ( Amazon ): Avoid type-switching (regardless whether you do it with an if-else ladder and dynamic_cast , or a switch statement with getID() function). Prefer instead to rely on polymorphism via virtual functions

class A
{
public:
  ~virtual A() = 0;

  void fun()  // non-virtual
  { 
     // main algorithm here

     // post-processing step
     post_fun();
  }

  virtual void post_fun() = 0;
};

class B : public A
{
public:
   virtual void post_fun() { /* bla */ }
};

class C: public A
{
public:
   virtual void post_fun() { /* meow */ }
};

A* a = a_list.front();
a->fun(); // will be resolved at run-time to whatever type a points to

The reason is that having an explicit type-switch is hard to maintain and update. If you get a new derived class from A , you need to update every place where you loop over types. Instead, the compiler will do that automatically for you if you rely on virtual functions.

In most cases, when you need to use anything B -specific (keeping your naming), you should store B * (or shared_ptr<B> , rather), instead of A * . In all other cases, hide everything behind polymorphism.

Consider following hierarchy:

class Animal 
{
public:
    Animal() {}
    virtual ~Animal() = 0;
    virtual void Breathe();
};

class Bird : public Animal
{
public:
    Bird() {}
    virtual ~Bird() {}
    virtual void Breathe() {...}
    virtual void Fly() {...}
};

and imagine you're storing Animal * s - you shouldn't call Fly() now. If you need to call it, then store Bird * from the beginning. But all animals must breathe - that's why the function is inherited from base class.


To sum it up: if you need to do something Child -specific, store pointer to Child , not to Base .

Usually, dynamic cast are perform to get specific type of object to work with them.

struct base
{
    virtual void a() = 0;
};

struct foo : base
{
    virtual void a() { ... }
    void specificFooMethod();
};

struct bar : base
{
    virtual void a() { ... }
    void specificBarMethod();
};

base * pBase = ...;
pBase->a(); // no casting required here
if ( foo * p = dynamic_cast<foo*>(pBase) )
{
    p->specificFooMethod();
}
else if ( bar * p = dynamic_cast<bar*>(pBase) )
{
    p->specificBarMethod();
}

specific* methods are not virtual and can't be acccessed by base interface without casting. So dynamic_cast should be used when you really need specific type of object which have additional functionality/properties that can't be move to abstract layer in base class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM