Strange behavior of virtual methods in inherited classes

Here is attached my simplified project to show strange behavior of compiler.
If you compile this project, you’ll get error message:
“undefined reference to `fmMenuItem::ProcessKey(int)’
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino/Genuino Uno.”

if you comment any of “a = 0;” / “b = 0;” / “c=2;”, the error disappears and compilation goes ok.

Here is the code if you can’t download the project zip file.

// ino file
#include "FormMenu.h"

void setup() {
   fmMenuItem* mi = new fmMenuNumber() ; 
}

void loop() {}
//FormMenu.cpp
#include "FormMenu.h"

fmMenuItem::fmMenuItem()
{
  a = 0;   //Comment this one
  b = 0;   //or this one
}

fmMenuOption::fmMenuOption( ):fmMenuItem()
{  
  c=2;
}

fmMenuNumber::fmMenuNumber() :fmMenuOption( ) {}

int fmMenuNumber::ProcessKey(int pKey) {return(0);}
//FormMenu.h
#ifndef FORMMENU_h
#define FORMMENU_h

class fmMenuItem
{
  public :
    int a;
    int b;
    int c;  
    virtual int ProcessKey(int pKey);
        fmMenuItem();
};

class fmMenuOption : public fmMenuItem
{
  public :   fmMenuOption();
};

class fmMenuNumber : public fmMenuOption
{
  public :  
      fmMenuNumber();  
   int ProcessKey(int pKey);
};

#endif

I guess there is some other problem that confuses compiler.
(IDE version 1.6.9)

test_virtual1.zip (827 Bytes)

When you defined ProcessKey() as a virtual function, you said that any class that derived from fmMenuItem would implement that method.

fmMenuOption derives from fmMenuItem, but does NOT implement ProcessKey(). Why not?

Because, I do not create instance of that class fmMenuOption directly, I just create inherited class fmMenuNumber, where the function is defined. Anyway, why does the compiler behave so strange?

Anyway, why does the compiler behave so strange?

Before you blame the compiler for not playing by the rules, you should.

Whether you NEED the ProcessKey() method in the derived class, or not, does not matter. You agreed, by deriving from a class that has one or more virtual methods, that you would implement the methods. You don't need to make them do anything useful, but you DO need to implement ProcessKey() in ALL derived classed.

After that, if there is still unexplained behavior, we will at least know that you are holding up your end of the agreement.

Derived classes do not have to implement all virtual functions themselves. They only need to implement the pure ones. Is my function pure virtual function?

Derived classes do not have to implement all virtual functions themselves. They only need to implement the pure ones. Is my function pure virtual function?

No, but it would have taken far less time to add a useless implementation for ProcessKey() in fmMenuOption to see if that resolves the issue than to argue about whether or not you need to.

I can't go that way and there are reasons, my example is simplified to show the problem, you trying to show way out avoiding some IDE problem. If I'm sure there is some issue with the IDE (and what kind of issue), I'll try to avoid it myself (of course there are many ways to do something in cpp in different ways, just I need to know to avoid this way in future).

The compilation stage will report no errors since the code is syntactically correct. On the other hand the linker will decide whether or not it needs implementations of the virtual methods. If virtual methods are not used the linker is free to ignore or not the fact that the virtual methods have not been implemented. The same argument applies to unused instance variables.

I'll try to simplify my task. Suppose there is class Animal with virtual function Eat. And there is derived class Predator Predator has ancestors Lion and Tiger with definition of Eat Now I create a Lion instance and pass it to some procedure as Predator (that is defined in Predator class - (Predator)Lion. Now I can call ((Predator)Lion).Eat() that is defined in Lion.

If virtual methods are not used the linker is free to ignore or not the fact that the virtual methods have not been implemented.

It appears as though the linker is saying that a virtual method has not been implemented.

Even though this might be an example program to prove that an issue exists, the logical next step is to implement the method and see how that changes the behavior, in the sample program.

If it solves the problem, you know what you need to do in your real program.

I can't go that way and there are reasons

You may have to.

kaor: I'll try to simplify my task. Suppose there is class Animal with virtual function Eat. And there is derived class Predator Predator has ancestors Lion and Tiger with definition of Eat Now I create a Lion instance and pass it to some procedure as Predator (that is defined in Predator class - (Predator)Lion. Now I can call ((Predator)Lion).Eat() that is defined in Lion.

That's a simplification?

Predator has ancestors Lion and Tiger with definition of Eat

This suggests that Predator is deriving from two different base classes. That causes a whole load of new issues with regards to virtual functions.

PaulS: This suggests that Predator is deriving from two different base classes. That causes a whole load of new issues with regards to virtual functions.

Not ancestors, just derived classes, I wrote mistakenly.

Animal -> Predator -> Lion / Tiger

You have not given the base an implementation.

Change the declaration in your base 'fmMenuItem' to an inline function:

virtual int ProcessKey(int pKey)[color=red]{return 0;}[/color]

As the intermediate and base interfaces are not abstract (you can create an instance of them), you must provide for the chance that the virtual function may be called on one of those objects, therefore your code needs an implementation somewhere, which in this case is the base.

Basically there has to be an implementation available to every non-abstract step in the inheritance tree.

...Or you could make the inherited bases abstract, which is what pure virtual functions do.

I've made base (fmMenuItem) virtual function pure and compiler ate it. In the middle base class fmMenuOption, there is still no function definition, just in derived class fmMenuNumber. So I've changed

virtual int ProcessKey(int pKey);

to

virtual int ProcessKey(int pKey)=0;

I'll test in details later.

More workaround revealed interesting details. Error reappeared after adding another virtual function. Compiler demands to define it in abstract middle base class, but I need it defined in derived functions.

Here again, let's suppose we have fmMenuItem abstract class with pure virtual function virtual void f1()=0; fmMenuOption class based on fmMenuItem; and fmMenuNumber class based on fmMenuOption with f1() implementation;

fmMenuOption* mi;
...
mi = new fmMenuNumber(...);
mi->f1();

Works fine. Note that if I define f1() in fmMenuOption, then mi->f1() will call fmMenuOption implementation of the function, not fmMenuNumber as I need.

Now when I added another virtual function f2() to base class, error reappeared, demanding to define the new function in fmMenuOption, the interesting thing is that after declaring f2 as pure virtual, problem disappeared, but base class fmMenuItem were abstract already due to virtual void f1()=0.

Can you post your code and error messages.

From what I understand, you have a setup similar to my code below. This code compiles fine.

struct A{
  virtual void foo() = 0;
  virtual void foo1();
};

struct B : A{};

struct C : B{
  void foo(){}
  void foo1(){}
};

//B b;  //<< Error abstract class.
C c;    //<< OK

void setup() {

  B &b = c;
  b.foo();
  b.foo1();
}
void loop() {}

There is my code in the firsts post of the topic. There is no pure abstract function in my original code as in yours.

My solution is to force all classes with virtual functions to abstract classes via pure virtual functions. Moreover, I discovered that sometimes leaving not pure function (even in abstract class, with other pure virtual function) causes the same problem, so now all my virtual functions are pure (at last most of them unless problem appears again).

kaor: There is my code in the firsts post of the topic. There is no pure abstract function in my original code as in yours.

I realize this. However, I was asking to see the code you are using now not the code you started with.

kaor: So I've changed

virtual int ProcessKey(int pKey);

to

virtual int ProcessKey(int pKey)=0;

To make a pure virtual method with no implementation, you have to give the compiler a definition of the method like so:

class fmMenuItem
{
  public :
    virtual int ProcessKey(int pKey) = 0;
};

If you split the class into a .h and a .cpp, then you put it in the .cpp like so:

fmMenuItem::ProcessKey(int pKey) = 0;

This tells the compiler to not go looking for the definition of the function.[/code]