C++ Inheritance on DUE and Strange Instance Variable Behavior

Scratching my head on this one…

I’m not a C/C++ guy but I’ve got a good many years of professional programming experience under my belt.

There’s a decent chance that my lack of C++ expertise may be the ultimate issue here…

In anycase, I’m testing out some extremely simple C++ inheritance examples and getting strange member variable behavior which I’m hoping someone here might be able to help with.

I’ve attached Archive.zip which includes all the files i’m running for a simple sketch named ‘Sandbox’.

The simple question I have is why doesn’t the serial output read ‘1,2,3,…’ instead of ‘2,2,6,6,14,14,…’

//////////////////////////////////////////
Relevant code from Sandbox.ino:

#include <SuperClass.h>
#include <SubClass.h>

SubClass S;

void setup()
{
Serial.begin(9600);
}

void loop()
{
S.loop();
}

/////////////////////////////////////////////////
Relevant code is from SubClass.cpp:

#include <SubClass.h>
#include <SuperClass.h>

SubClass :: SubClass() : SuperClass()
{
this->x1 = 2;
this->x2[this->x1];
this->x3 = 0;

for(int i1=0; i1x1; i1++) {
this->x2[i1] = 0;
}
}

SubClass::~ SubClass(){}

void SubClass::loop()
{
this->x3++;
for(int i1=0; i1x1; i1++) {
this->x2[i1] += i1+this->x3;
}
delay(500);
for(int i1=0; i1x1; i1++) {
this->f1(i1);
}
}

void SubClass::f1(int i1)
{
Serial.println(this->x3);
}

Archive.zip (3.69 KB)

Your inheritance setup is fine. However, you have a problem with your member variable x2: it is never dimensioned. I think you're attempting to dimension is with the line "this->x2[this->x1]" but that is incorrect, that is interpreted as "read x2[x1]", where x1 = 2, therefore it'll run as "read x2[2]", which will do nothing since you're not even assigning the result, so the compiler would likely optimize it away.

Something you need to know about C/C++ is that it doesn't really "do" arrays in the same way that other languages do, they're essentially just syntactic sugar around pointers, which means you can't dynamically resize arrays at runtime in the way you're attempting to do so. They also don't do bounds-checking. Consequently, x2 is never being properly initialized: No memory is being allocated for the contents of the array, and the x2 pointer itself is also undefined. So, when you subsequently assign to members of this array (writing zeroes to it in the constructor, and then doing that addition to it in loop) you're not actually writing to x2, because x2 doesn't exist. You're writing to some arbitrary other piece of memory, which could literally be anything. This is likely the source of your problems.

You need to dimensiont the array in the place where its declared, which is in SubClass.h on line 13: "x2," should read "x2[2],". Note you can't use x1 here because x1 is a runtime non-constant variable, so you'd need to make a constant or a macro to store this information if they're related, e.g...

class SubClass : public SuperClass {
public:
        SubClass();
        ~SubClass();

        const int ARRAY_SIZE = 2;

        int 	x1 = ARRAY_SIZE,
                x2[ARRAY_SIZE],
        	x3;

        virtual void loop();
        void f1(int i1);
};

If in future you actually want to be able to dynamically resize arrays, you do that by creating and deleting memory to the pointer:

const int size1 = 5;  //Size of the array I want.
const int size2 = 10;

int* foo;  //Declaration. No memory assigned. Accesses will be undefined.
foo[2] = 56;  //WRONG.
foo[7] = 56;  //WRONG.

foo = new int[size1]; //OK, now foo points to a valid array with five members.
foo[2] = 56;  //Good!
foo[7] = 56;  //WRONG.

delete[] foo;  //We've deallocated the foo array, so now accesses will be undefined.
foo[2] = 56;  //WRONG.
foo[7] = 56;  //WRONG.

foo = new int[size2]; //OK, now foo points to a valid array with ten members.
foo[2] = 56;  //Good!
foo[7] = 56;  //Good!

delete[] foo;  //We've deallocated the foo array, so now accesses will be undefined.

Note the use of delete is very important otherwise you'll introduce memory leaks because you'll be allocating memory but never deallocating it. When you get into dynamic memory allocation in C++, YOU the programmer are responsible for managing it, neither the runtime environment nor the compiler will help you.

I hope all that helps and is not an information overload! :slight_smile:

Makes perfect sense, thank you alarmsiren!

++ djjb

As a followup, to get the dynamic array size allocation working (compiling) correctly I needed to change my declaration of x2 in SubClass.h

// to
int x1,
*x2,
x3;

// from
int x1,
x2,
x3;

Ah yes, you're right. I must admit I did not actually compile the code I gave as examples, and I missed that point. I've corrected my post to reflect that reality. However, you can still treat the pointer subsequently as if they're an array...

int * x2 = new int[5];

//valid!
x2[2] = 56;

//equivalent to
*(x2 + 2) = 56;

Also another subtlety that I glossed over is that "delete" and "delete" are different, and you have to make sure you get the right one. "delete" is for deallocating arrays created with the new keyword, as we've been doing, and "delete" is for just the plain "new", i.e. when you create a single object rather than an array.

int* foo = new int;
int* bar = new int[5];

delete foo;
delete[] bar;