Initialization of Class-type Member Data

If I understand correctly, in C++, you cannot initialize private data directly in the class definition. Hence, this does not work:

class Foo {
  private:
    int k = 0;
};

As with the above simple example, you cannot initialize a member that is another class. Again, this does not work either:

class Foo {
  public:
    Foo(int i);
};

class Bar {
  private:
    Foo foo(13);  // does not work
};

I can split the instantiation and the initialization into two steps:

class Foo {
  public:
    void init(int i) {
      // do stuff with i
    } 
};

class Bar {
  public:
    Bar() {
      foo.init(13);
    }
  private:
    Foo foo;
};

But what do I do if class Foo is out of my control (eg. from a lib), and if there's only a parameterized constructor? I guess I could work with a pointer to the foreign class, but as the 'new' operator is not implemented for AVR, I cannot create an instance to point to from within my own class. Hence, the only workaround I see, is to create the instance of Foo "outside" of my class and pass it as parameter upon creation of the instance of Bar, like so:

class Foo {
  public:
    Foo(int i) {}
};

class Bar {
  public:
    Bar(Foo* f) {
      foo = f;
      // more init stuff
    }
  private:
    Foo* foo;
};

Foo foo(13);
Bar bar(&foo);

However, I might miss something here, and would be grateful to get some advice of what is state-of-the-art for my problem in the Arduino/AVR context.

Thanks! -- Stefan

If I understand correctly, in C++, you cannot initialize private data directly in the class definition. Hence, this does not work

This is true.

As with the above simple example, you cannot initialize a member that is another class. Again, this does not work either:

Why not? You are passing a value to the constructor that is defined to take a value. Without seeing the code for the constructor, I see no reason why that would not work.

I guess I could work with a pointer to the foreign class, but as the 'new' operator is not implemented for AVR, I cannot create an instance to point to from within my own class.

By default, it isn't. That doesn't mean that it can't be. There have been a number of posts explaining how to implement new and delete. Generally not the best practice, but if you are aware of the limitations, and can live with them, you can implement them.

But what do I do if class Foo is out of my control (eg. from a lib), and if there's only a parameterized constructor?

You need to whack the Foo developer upside the head for not creating a library that can be used within another class.

PaulS:

As with the above simple example, you cannot initialize a member that is another class. Again, this does not work either:

Why not? You are passing a value to the constructor that is defined to take a value. Without seeing the code for the constructor, I see no reason why that would not work.

Like so?

class Foo {

public:
    Foo(int i) {k = i;}
   
  private:
    int k;
};

const int baz = 13;

class Bar {

private:
    Foo foo(baz);
};




Does not compile OMM ("error: 'baz' is not a type").

Because member variables (foo) can not be initialized in the header file, the compiler knows that you are not trying to assign foo a value (by calling its constructor), so, you must be trying to define a function named foo(), returning something of type Foo, taking one argument of type baz.

baz isn't a type, as the compiler adroitly notes.

Short of implementing new and delete, I think your only solution is to define foo as a global variable.

Thanks. I understand what's going on, but I am rather baffled: isn't the use of a class-type data member within a class a pretty normal situation? Is the limitation summarized correctly as follows: any class that only has parameterized constructors cannot be used to declare and define a data member in another class without workarounds if there no operator 'new'.

If that's correct, I guess I need to learn a lesson and always provide a (possibly additional) parameterless constructor plus a corresponding init function for my own classes...

Yes, your summary is correct.

Can't you use constructor initialization lists?

class Bar {
  private:
    Foo foo;
  public:
    Bar() : foo(13) {}
};

That seems to work! At least this code:

class Foo {
  public:
    Foo(int i) {k = i;}
    int f() {
      return k; 
    }
  private:
    int k;
};

class Bar {
  public:
    Bar(int x, int y) : foo(x), l(y) {} // {l = h;}
    int b() {
      return foo.f() + l;
    }
  private:
    Foo foo;
    int l;
};

Bar bar(13, 29);

void setup() {
  Serial.begin(9600);
  Serial.println(bar.b());
}

void loop() {
}

...prints '42'. Cool.

Thanks!

sbr_:
I guess I could work with a pointer to the foreign class, but as the 'new' operator is not implemented for AVR, I cannot create an instance to point to from within my own class.

Don't fret too much:

// new
inline void * operator new (size_t size) { return malloc (size); }
// placement new
void * operator new (size_t size, void * ptr) { return ptr; }
// delete
void operator delete (void * ptr) { free (ptr); }

Be aware of static initialization order issues. You are better off having a constructor which does nothing non-trivial, and then adding a "begin" function to initialize your class (called from setup or some useful place).

Sure, in particular also if your init code relies on hardware facilities that might not be initialized yet. However, consider using PString (PString | Arduiniana) inside your class, which you need to instantiate with a parameterized constructor:

#include "PString.h"
char buf[10];
PString ps(buf, sizeof(buf));

In my constructor, I do, in simplified example:

#include "PString.h"
class Bar {
  public:
    Bar(): ps(buf, sizeof(buf)) {}
  private:
    char buf[12];
    PString ps;
};

Bar bar;

How would you do this with your 'begin' function?

I am not an expert, but that does not support the creation and initialization of new class instances, like:

class Foo {
  public:
    Foo(int i) { /* init stuff */ }
};

Foo* foo = new Foo(13);

Or does it?

Don't have time to do up a test, but it will work fine for the code below

int *a = new int( 13 );

So I would assume yes.

pYro_65:
Don't have time to do up a test, but it will work fine for the code below
So I would assume yes.

Indeed:

inline void * operator new (size_t size) { return malloc (size); }
void * operator new (size_t size, void * ptr) { return ptr; }
void operator delete (void * ptr) { free (ptr); }

class Foo {
  public:
    Foo(int i) {k = i;}
    int f() {return k;}
  private:
    int k;
};

Foo baz(17);

void setup() {
  int *a = new int(99);
  Foo* foo = new Foo(13);
  Serial.begin(9600);
  Serial.println(*a);
  Serial.println(foo->f());
  Serial.println(baz.f());
  delete(foo);
  delete(a);
  Serial.println("done");
}

void loop() {}

prints:
99
13
17
done

Cool.

sbr_:
I am not an expert, but that does not support the creation and initialization of new class instances, like:

...

Or does it?

Yes, that is why I suggested it. Otherwise might as well use malloc.

sbr_:
How would you do this with your 'begin' function?

Unfortunately your example includes a class, PString, which itself doesn't have a begin function (or at least not one that sets up the class).

So in this case Bar can't use a "begin" function to allocate a variable length string, because the PString constructor needs the parameters supplied at constructor time.

However I don't see we would get very far even if it did. For the PString class to have a variable length buffer (allocated by the "begin" constructor) you would need to use dynamic memory allocation, so you may as well use the String class anyway, which does that.

To illustrate the technique however:

#include "PString.h"

// new
inline void * operator new (size_t size) { return malloc (size); }
// placement new
void * operator new (size_t size, void * ptr) { return ptr; }
// delete
void operator delete (void * ptr) { free (ptr); }

class Bar {
  public:
    void begin (int length)
      {
      buf = (char *) malloc (length); 
      ps = new PString (buf, length);
      };
      
  private:
    char * buf;
    PString * ps;
};

Bar bar;

void setup () 
  {
  bar.begin (20);
  }

void loop () {}

Well, PString was only an example to demo a typical class without parameterless constructor. For our discussion, just assume that in our example we want a fixed-length PString buffer. However, as pointed out by pYro_65, you don't need to resort to dynamic instantiation and memory allocation, by using a constructor initialization list to init such class, as in this simplified demo example:

class Foo {
  public:
    Foo(int i) {k = i;}
    int f() {return k;}
  private:
    int k;
};

class Bar {
  public:
    Bar(int x, int y) : foo(x), l(y) {}
    int b() {return foo.f() + l;}
  private:
    Foo foo;
    int l;
};

Bar bar(13, 29);

So we have 1) parameterless constructor + begin() type init function, 2) constructor initialization lists, and 3) dynamic creation using the new and delete function that you presented to solve my original problem -- without the need for workarounds.

Thanks for all the constructive suport! :slight_smile:

sbr_:

Bar bar(13, 29);

Er, yes, but you still are creating it statically. What if 13 and 29 are derived at runtime (eg. from serial input)? Then how do you create it?

You can't statically create it because you don't have the parameters yet, and if you put it into a function (like loop) then its scope is only inside that function.

Er, yes: my point is, and was, that I want to create it statically! I of course understand what you're saying -- and I appreciate your input and insights -- but that was not my question! The challenge was to create and initialize a data member, without the help of dynamic memory allocation, if the corresponding class does not offer a parameterless constructor and some init() function. :slight_smile:

I'm not sure I want to answer hypothetical questions about foo and bar. Can you give a concrete example of what you are trying to do rather than just issuing challenges?