Why are the methods of the descendant classes not called?

Created three classes. The second is inherited from the first, the third is inherited from the second. The first declares a virtual “empty” method. I refer to it in the first class. But it is not there and should not be, it will be redefined at the very end of the hierarchy. In the third class, this method is finally overridden with the override keyword. I create a third class instance. BUT IT DOESN’T CALL AN OVERDID METHOD, BASTARD! He tries to pull the implementation out of the first class. What is the problem?

The base class, two virtual methods are declared in it (circled in red). One of them is called inside the constructor. But it has to do the job specific to the target instance:

Next class. It does not have these methods:

And finally, from the second class, the third comes into the light. It redefined these methods:

What’s wrong?

This could be the problem. C++ constructs instances starting with the superclass constructor and then continues with constructors along the hierarchy until the most specific class’ constructor is executed. Until the constructor of a class has finished, neither the member variables nor the member functions of a subclass can be called. For virtual functions it means that the function of the class being currently constructed is called.

An example:

#include <iostream>

using namespace std;

class A {
public:
    A() { whoAmI(); }
    virtual void whoAmI() { cout << "I'm class A" << endl; }
};

class B : A {
public:
    B() { whoAmI(); }
   void whoAmI() override { cout << "I'm class B" << endl; }
};

class C : B {
public:
    C() { whoAmI(); }
   void whoAmI() override { cout << "I'm class C" << endl; }
};

int main() {
  C instance;
  return 0;
}

This will produce the output:

I'm class A
I'm class B
I'm class C

In your specific case it means that DHTxx’s implementation of initSensorValues() and readRawData() cannot be called from the rSensor and rSensorX2 constructors.

I didn’t know that everything in C ++ is so bad.
On Delphi, it works without problems for one or two.
What, now fence 100500 methods ???

You can’t expect that C++ works exactly the same like Delphi. C++ object initialization is based on a sound concept, and as part of that concept, calling subclass functions from the superclass constructor makes no sense.

Not as a dispute, but as a reasoning …

In my opinion, this is the only meaning of classes and inheritance.
This only forces the initialization of the device not immediately in the constructor, but then in some other place.
Now the approach has become clear to me, why in arduino almost all classes are initialized not in the constructor, but in the additional method begin ()
Two calls instead of one. This is stupid. However, I have to take it for granted.

This might be one of the reasons Arduino classes initialize in two steps. But there is a more important one relevant in all embedded systems:

In embedded systems, you usually want to create objects as global or static instances instead of allocating them on the heap. Global and static instances are initialized very early in the startup of the program - before the Arduino main() and before the call of setup(). However, basic MCU initialization (e.g. configuring the clocks) is done in main. Thus, most global or static instances will fail if they access peripherals in the constructor. To solve it, the constructor is minimal and does not access peripherals. Instead, a separate init() method is provided, which is called later (e.g. from setup()).

I got it, thanks for the detailed explanation

Hmm … interesting. This leads to thoughts…
Sorry, I’ll distract you a little more

In my case, there are two more instances of another class “inside” DHTxx - a kind of data storage with time stamps, filters (so far only mean and median) and extrema. If I allocate the sensor as static, then how can I allocate inline elements also not on the heap, but statically? Probably not? They are now being allocated on the heap.
PS: I don’t see much sense in avoiding dynamic allocation of objects in the heap, because the ESP32 stack is divided between tasks and can easily be overflowed, and the heap is shared. Or am I misunderstanding something?

Member variables (primitive types and objects) of static and global objects are part of the same allocation and therefore inherit the allocation (global/static, heap, stack) from the parent. If the member variable is a pointer, it applies to the pointer only, not to the element referenced by the pointer.

Static and global instance are not on the stack. So your reasoning regarding overflow is not correct.

Having said that, the ESP32 has plenty of memory and avoiding heap allocation is not a major goal on this platform. But most Android libraries are written to be used on multiple platforms, including far more restricted ones than ESP32.

Thank you so much.