Nor base nor derived virtual function being properly called

99 views Asked by At

I have this base class:

// put the display in a macro on a .h file for less headache.
class Gadget {
  protected:
    int x, y;
    U8GLIB * u8g;

    virtual int  f_focus()  {return 0;};
    virtual int  f_blur()   {return 0;};
    virtual void f_draw()   {};
    virtual void f_select() {};


  public:
    Gadget(U8GLIB * u8g, int x, int y) :
      u8g(u8g),
      x(x),
      y(y)
    {
      Serial.println(F("Gadget(U8GLIB * u8g, int x, int y)"));
    };

    Gadget() {
      Serial.println(F("Gadget()"));
    };

    int     focus(){return f_focus();};
    int     blur(){return f_blur();};
    void    draw(){f_draw();};
    void    operator()(){f_select();};
};

And this derived class:

class WakeUp :
  public Gadget
{
  public:
    WakeUp(U8GLIB * u8g) :
      Gadget(u8g, 0, 0)
    {
      Serial.println(F("WakeUp(U8GLIB * u8g)"));
    };

};

Then I instantiate the WakeUp class inside an array like this:

Gadget gadgets[1] = {
  WakeUp(&u8g)
};

Then I try to access this member like this:

void focus() {
  Serial.println(gadgets[0].focus());
}  

It is supposed to display 0. However it is displaying -64. Even if I override the f_focus() method on WakeUp class. If I remove the virtual specifier from f_focus() it works fine, displaying 0, but I will not be able to access the derived class implementation of this method. I wish to understand what is causing this strange behavior and what can I do to avoid it.

EDIT:

The function runs fine if I call it from the Gadget Constructor.

1

There are 1 answers

3
lcs On BEST ANSWER

You're slicing your WakeUp object.

You essentially have the following:

Gadget g = WakeUp(...);

What this code does is the following:

  1. Construct a WakeUp object.
  2. Call Gadget(const Gadget& other) with the base from the WakeUp object.
  3. Destroy the temporary WakeUp object, leaving only the copy of the Gadget base.

In order to avoid this, you need to create an array of pointers (this is better if they are smart pointers).

Gadget* gadgets[1] = { new WakeUp(&u8g) }; // If you choose this method, you need to call 
                                           // delete gadget[0] or you will leak memory.

Using a pointer will correctly preserve the Gadget and WakeUp instances instead of slicing them away.

With smart pointers:

std::shared_ptr<Gadget> gadgets[1] = { std::make_shared<WakeUp>(&u8g) };