Class and constructor.

Hello to our forum !

Iam watching online C++ lessons and when it comes to arduino i have a question. Actually i have tons of questions but for now i will express this noe :slight_smile: . Is the object constructor called automatically in arduino or do i have to manually call it inside the setup function ?

Thank you!

It isn't called automatically. You also don't have to call it in the setup. You have to call it in the space above void setup() where you also include all the libraries.

The constructor is executed when an object is created.

You should not (and cannot) call the constructor manually.

You can experiment with constructors and destructors yourself to see when they're called: Compiler Explorer

Note that there is nothing special about the "setup" function. It's a normal function that is called by the Arduino Core, in short:

int main() {
  init(); // hardware initialization
  setup(); // user setup
  while (1) { // infinite loop
    loop();
  }
}

Pieter

Thank you all for your help.

ArnavPawarAA:
It isn't called automatically. You also don't have to call it in the setup. You have to call it in the space above void setup() where you also include all the libraries.

I am a little confused. What do you mean i have to call it in the space above void setup() where i also include all the libraries ?

What do you mean i have to call it in the space above void setup() where i also include all the libraries ?

The call to the constructor of a class needs to be done at global level rather than in a function if you want the object to be available throughout the program

It is good practice for the object to be created by the constructor globally then for there to be a begin() method called from inside setup() that initialise the object with pin numbers etc. Doing it this way ensures that the hardware is ready before setting pinMode()s etc If you do that in the constructor then there is a chance that the hardware is not ready

HellasT:
Thank you all for your help.

I am a little confused. What do you mean i have to call it in the space above void setup() where i also include all the libraries ?

You have to construct just after including the libraries. You don’t have to call it in the setup. You have to construct after including the libraries.

Like this:

#include  <LiquidCryshal.h>
#include <sample.h>
int pins = 4, 5, 8, 9;

//Construct all here


void setup(){}
void loop(){}

As already pointed out, you never "call" the constructor. It is implicitly called when you instantiate the object. That can be done using the same syntax as when you define a variable:

myClass myObject;

If this is done outside of any function, the object is global scope. If done inside a function, it is local scope to that fucntion.

The constructor can also be implicitly called when you instantiate the object dynamically. Again, this is similar to dynamic allocation of a regular variable:

myClass *myObjectPtr = new myClass;

This can only be done inside a function, however, the pointer may be defined globally:

myClass *myObjectPtr;

void setup() {
  myObjectPtr = new myClass;
}

void loop() {
}

UKHeliBob:
The call to the constructor of a class needs to be done at global level rather than in a function if you want the object to be available throughout the program

It is good practice for the object to be created by the constructor globally then for there to be a begin() method called from inside setup() that initialise the object with pin numbers etc. Doing it this way ensures that the hardware is ready before setting pinMode()s etc If you do that in the constructor then there is a chance that the hardware is not ready

I wrote a sketch that is supposed to blink a led every 1 sec.
So i wrote a class blinker and created an boject LED1 that takes the ledpin number as argument.

Is that what you are suggesting ? Am i getting it right ?

I have uploaded it on an uno and it works all right.

P.S.
This is the very first class i ever wrote myself :stuck_out_tongue:

class blinker
{
  public:
    blinker (byte define_LedPin);  
    void blink_();
  private:
    byte LedPin;
};
blinker LED1(13);

void setup() 
{
 
}

void loop() 
{
  LED1.blink_();
}

blinker::blinker (byte define_LedPin)
{
  LedPin = define_LedPin;
  pinMode (LedPin, OUTPUT);
}

void blinker::blink_()
{
  digitalWrite (LedPin, HIGH);
  delay (1000);
  digitalWrite (LedPin, LOW);
  delay (1000);
}

Looks like you missed this:

UKHeliBob:
It is good practice for the object to be created by the constructor globally then for there to be a begin() method called from inside setup() that initialise the object with pin numbers etc. Doing it this way ensures that the hardware is ready before setting pinMode()s etc If you do that in the constructor then there is a chance that the hardware is not ready

gfvalvo:
Looks like you missed this:

UKHeliBob:
It is good practice for the object to be created by the constructor globally then for there to be a begin() method called from inside setup() that initialise the object with pin numbers etc. Doing it this way ensures that the hardware is ready before setting pinMode()s etc If you do that in the constructor then there is a chance that the hardware is not ready

I am not sure i understand completely.
Would you please be kind enough to modify my code and show me what you mean ?
Thank you !

HellasT:
I am not sure i understand completely.
Would you please be kind enough to modify my code and show me what you mean ?
Thank you !

class blinker {
  public:
    blinker (byte ledPin) : ledPin(ledPin) {}
    void begin();
    void blink();
  private:
    byte ledPin;
};

blinker LED1(13);

void setup() {
  LED1.begin();
}

void loop() {
  LED1.blink();
}

void blinker::begin () {
  pinMode (ledPin, OUTPUT);
}

void blinker::blink() {
  digitalWrite (ledPin, HIGH);
  delay (1000);
  digitalWrite (ledPin, LOW);
  delay (1000);
}

Okay i think im starting to understand but i do have an other question.

How exactly does this work ?

blinker (byte ledPin) : ledPin(ledPin) {}

blinker (byte ledPin) is obviously the constructor.
And this ? : ledPin(ledPin) {}

It's a member initializer list.

Okay. I clearly need to study more on that so i think i will bother you no more about it at the moment.

Something else. The words blink and begin are reserved key words right ? Is it okay if i use other names ? for example blinkg_ ? Iam going to try it anyway and see.

Thank you alot for your help !

PieterP:
It's a member initializer list.

Here's a less pedantic resource: When do we use Initializer List in C++? - GeeksforGeeks

HellasT:
Something else. The words blink and begin are reserved key words right ? Is it okay if i use other names ? for example blinkg_ ? Iam going to try it anyway and see.

No, they are not reserved keywords. The colors in the Arduino IDE are mostly meaningless, it just matches the words with a long list of function names of installed libraries. If it turns orange, it simply means that the name (blink for example) is used somewhere in some library, you can use it without any problems.

The only identifiers that are truly reserved in C++ are specified here: C++ keywords - cppreference.com
There are other identifiers that you cannot use in Arduino, because of some macros that are defined in the Arduino core, such as HIGH and LOW, for example. AFAIK, there's not really a comprehensive list, but the compiler will probably complain if you try to use them.

I think i am begining to understand one bit at a time.
So if i dont use member initializer and instead i write this:

class blinker
{
  public:
    blinker (byte define_LedPin);
    void begin ();  
    void blink_();
  private:
    byte LedPin;
};
blinker LED1(13);

void setup()
{
  LED1.begin();
}

void loop()
{
  LED1.blink_();
}
void blinker::begin()
{
  pinMode (LedPin, OUTPUT);
}

blinker::blinker (byte define_LedPin)
{
  LedPin = define_LedPin;  
}

void blinker::blink_()
{
  digitalWrite (LedPin, HIGH);
  delay (1000);
  digitalWrite (LedPin, LOW);
  delay (1000);
}

Is it the same just written in more lines of code or is it that member initializers serve other purposes also ?

and the reason we want to call the begin function inside setup is to make sure that the arduino has reached the point where all hardware is ready for use yes ?

HellasT:
Is it the same just written in more lines of code or is it that member initializers serve other purposes also ?

Initializer lists can initialize anything. The approach you used (with the assignment in the constructor) can only be used for types that have a default constructor.

You can see the difference here: Compiler Explorer

The code has a class “S” that prints when an object is created, when it is copied, and when it is destroyed. Each instance of the class “S” has a unique ID.
Then there are two classes, Good and Bad, that have a member of type S. One uses an initializer list, the other does not.

In this example, “Good” and “Bad” are like “blinker” in your code, and their member “S s” is like the member “byte LedPin” in your “blinker” class.

The rest is some boilerplate for printing (std::cout, puts) and keeping the unique ID.

#include <iostream>

// Ignore this class for now, it's just a class that prints its unique ID when it 
// is created, copied or destroyed.
class S {
  public:
    S() : index(++instance_ctr) { std::cout << "Default constructor: "<< index << std::endl; }
    S(const char *name) : index(++instance_ctr), name(name) { std::cout << "Constructor with name: " << index << " (" << name << ")" <<  std::endl; }
    S &operator=(const S &other) { 
        this->name = other.name; 
        std::cout << "Copy assignment: " << this->index << " ← " << other.index << " (" << name << ")" <<  std::endl;
        return *this;
    }
    ~S() { std::cout << "Destructor:  " << index << " (" << name << ")" << std::endl; }

  private:
    int index = 0;
    const char *name = nullptr;
    static int instance_ctr;
};
int S::instance_ctr = 0;

// With an initializer list
class Good {
  public:
    Good(const char *name) : s(name) {}

  private:
    S s;
};

// Without an initializer list
class Bad {
  public:
    Bad(const char *name) {
        s = name;
    }

  private:
    S s;
};

int main() {
  puts("");
  puts("Creating 'Good'");
  { Good g = "Good"; }
  puts("");
  puts("Creating 'Bad'");
  { Bad b = "Bad"; }
}

The output:

Creating 'Good'
Constructor with name: 1 (Good)
Destructor:  1 (Good)

Creating 'Bad'
Default constructor: 2
Constructor with name: 3 (Bad)
Copy assignment: 2 ← 3 (Bad)
Destructor:  3 (Bad)
Destructor:  2 (Bad)

The class “Good” that uses the initializer list initializes its member “s” directly, you see one call to the constructor of “S”. Only one “S” object is created (with index 1).

The class “Bad” on the other hand first initializes its member “s” with the default constructor (without a name). This member “s” without a name has index 2.
Then it uses the name to creates a temporary “S” object (with index 3), and then copies the temporary (index 3) to its member “s” (index 2).

That’s the difference: initializer lists initialize the members directly, your version first constructs a default-initialized variable, and then copies the actual value into it.

Thank you so much for the useful information. Its always good to learn new things.
For now i have no time to study the code but i will do so tomorrow.

THANK YOU.