call parent virtual function from child class


Keywords:c++ 


Question: 

I want to make some "duel" with two "units". I write class "duel" that constructs from two "units". But some kind of "unit" is special (inherited from units) like heroes, bosses etc. And they want to use special strikes during battle. But actually class "duel" doesn't know who is hero, or who is pure unit.

Code looks like this:

#include <iostream>

class unit{
  public:
   unit(){};
   virtual void make_hit(){
     std::cout<<"pure hit\n";
   }
};

class hero:public unit {
  public:
   hero():unit(){};
   void  make_hit(){
     std::cout<<"SUPER hit\n";
   }
};

class duel {
  unit *a, *b;
  public:
  duel(unit _a, unit _b):a(&_a),b(&_b){};
  void start (){
    a->make_hit();
    b->make_hit();
  }
};

int main(){
  duel(unit(),hero()).start();
  return 0;

}

I have two main problem.

First - I use refers to temporary objects in constructor. That objects illegal when duel::duel() finished.

Second - my hero turned into pure unit, and doesn't use "SUPER hit"

Is it possible fix it in elegant way (without changing call in main())?


3 Answers: 

Due to slicing, it's better to always use polymorphism together with smart-pointers. This would be a possible design:

#include <iostream>
#include <memory>
#include <utility>

using namespace std;

class unit_base
{
public:
    virtual ~unit_base() = default;
    virtual void make_hit() =0;
};

class unit : public unit_base
{
public:
    unit() = default;
    virtual void make_hit() override
    {
        cout << "pure hit" << endl;
    }
};

class hero : public unit_base
{
public:
    hero() = default;
    virtual void make_hit() override
    {
        cout << "SUPER hit" << endl;
    }
};

class duel
{
public:
    duel( shared_ptr<unit_base> a, shared_ptr<unit_base> b )
        : a(a), b(b)
    {}
    void start()
    {
        auto aa = a.lock();
        auto bb = b.lock();
        if( aa && bb )
        {
            aa->make_hit();
            bb->make_hit();
        } else {
            cout << "duelist expired" << endl;
        }
    }
private:
    weak_ptr<unit_base> a, b;
};

int main()
{
    // use with temporarys
    duel{ make_shared<unit>(), make_shared<hero>() }.start();

    cout << "-------------" << endl;

    // use with variables
    auto u = make_shared<unit>();
    auto h = make_shared<hero>();
    duel d{h,u};
    d.start();

    cout << "-------------" << endl;

    // try to use with expired duelists
    u.reset();
    d.start();
}

Also remember to always have a virtual destructor in your base-class.



In

duel(unit _a, unit _b):a(&_a),b(&_b){};

You are slicing the objects as you are passing by value. To fix this you can take in pointers in your constructor

duel(unit* _a, unit* _b):a(_a),b(_b){};

And then you will need to change main() to create to objects and pass them to duel

int main(){
    unit npc;
    hero bob;
    duel d(&npc,&bob);
    d.start();
    return 0;
}


The only way I have found for myself in C++ is make all combination of constructor in "duel" class. This solution not so elegant, need changing in "duel" code when added new class, and also this have overhead.

  ~duel(){
    delete a;
    delete b;
  }
#define _cc(t1, t2) duel(t1 _a, t2 _b) : a(new t1 (_a)), b (new t2(_b)){}
  _cc(unit,unit);
  _cc(hero,unit);
  _cc(unit,hero);
  _cc(hero,hero);
#undef _cc

I also tried use template for this, but I can't find a way make automatic type determination.