using "const" in objects / classes

For "normal" Arduino sketches people recommend using "const" for variables that do not change, ie pin numbers. But how does this work with classes, where I pass an argument during initialization and never touch that later? Or is it useless there?

The compiler always gives the error "uninitialized const" when the declariation and initialization of a "const" variable happens at two separate places, such as declaration in the header file and initialization later.

A const, by definition, MUST be declared and initialized at one time. How is it a const, if you can still write to it AFTER it's been created? That would be like having a read-only memory that you could write to. In fact, that is exactly what it is, since all const variables are stored in FLASH.

Regards, Ray L.

It doesn't make sense to make an object (or "an instance of a class") a constant. You can, however, have a static class that exports constants. This is very common in Java, but I don't think it is very useful in Arduino, because it adds quite an overhead. It is best to have the constants defined using #define in a header file.

Remember that classes can have static members, which will persist among all of its instances.

If your code is initializing a typed constant twice, there is something wrong with it. Typed constants should only be declared and initialized once, and only once.

As AlxDroidDev points out, you could use a #define in the header file. The good thing about a #define is that it is typeless. The bad thing about a #define is that it is typeless. In other words, it does give you the flexibility to use it with any data type (e.g., #define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0])) ), but you give up the safety of type checking that the compiler makes available. If the Arduino had a debugger as part of its IDE, I'd favor a const, since it has an lvalue that could be used by a debugger; a #define does not. You need to determine which works best for your situation.

I read somewhere that declaring a variable "const" would improve code optimization done by the compiler.

Now for this to happen, the value of the variable must be known at compilation time, is that right? My previous assumption - maybe faulty - was that the optimization would also improve the code for a variable that is filled with some value at a certain point of runtime but never changed after that.

halfdome:
I read somewhere that declaring a variable “const” would improve code optimization done by the compiler.

It quite often will but it will only work if it really is CONSTANT. You can’t declare something as constant and then change it later.

Do you have an example of the code you’re trying to implement. Maybe we’ll be able to sort out the detail that’s eluding you. (fresh pairs of eyes will often spot an alternative method).

I have written a “Notification” library to allow for various LED blink patterns on different pins.

Upon first call / declaration I pass the

#include "Notification.h"

Notification led_1(13);
Notification led_2(12);

const uint16_t blink_pattern_1[] = {100, 200, 100, 600};
const byte sizeof_pattern_1 = 4;
...

void setup(){
    led_1.set_pattern(blink_pattern_1, sizeof_pattern_1);
    //pass blink patterns to object
   //similar for other led_i's

}
void loop(){
  led_1.check(); //oversees the blinking durations and switches LED on/off
  led_2.check();
  if (some condition) led_1.set_pattern(blink_pattern_2, sizeof_pattern2);
}
class Notification{
  public:
    Notification(int pin);
    void check();
    .....
  private:
    int _pin; // Declaration without initialization
}
Notification::Notification(int pin){
  _pin = pin; //initialization, not changed afterwards
}

The penultimate line in the above code made me inquire the “const” issue within classes. But obviously it is declared earlier.

PS: The full code is attached, however, while the LED part works, the loudspeaker part is not checked yet.

notification.cpp (1.77 KB)

notification.h (1.15 KB)

The compiler always gives the error “uninitialized const” when the declariation and initialization of a “const” variable happens at two separate places, such as declaration in the header file and initialization later.

E.g:
Notification::Notification(int pin){
** _pin = pin; //initialization, not changed afterwards**
}

Is not an initialization, but assignment!

Constants and references must be initialized. Which means using the constructor initializer list. Trying to set values to the constant inside the constructor is an assignment (and at that point, already initialized), and something which const is designed to prevent.

struct Foo{
** const int myConst;**

** Foo( const int val ) : myConst( val ) {**
** //Empty**
** }**
};

BTW you can still separate this into header and .cpp, you just need to use the constructor initializer list!

econjack:
The bad thing about a #define is that it is typeless. In other words, it does give you the flexibility to use it with any data type (e.g., #define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0])) ), but you give up the safety of type checking that the compiler makes available.

Sorry, no bannana!

** int x;**
** ELEMENTCOUNT(x);**

Fail! The C++ type system is never avoided. #defines are not typeless, it is a simple “copy ‘n’ paste” action of real code, which does get scrutinized by the compiler.

It doesn’t make sense to make an object (or “an instance of a class”) a constant

Of course it does. Every temporary you’ve ever used is a constant, because it doesn’t make sense to modify them.

econjack: As AlxDroidDev points out, you could use a #define in the header file. The good thing about a #define is that it is typeless. The bad thing about a #define is that it is typeless. In other words, it does give you the flexibility to use it with any data type (e.g., #define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0])) ), but you give up the safety of type checking that the compiler makes available. If the Arduino had a debugger as part of its IDE, I'd favor a const, since it has an lvalue that could be used by a debugger; a #define does not. You need to determine which works best for your situation.

define does not let you get around type checking. #defines are literal text substitutions that take place BEFORE the compiler ever sees the code. What the compiler sees is precisely what it would see if you edited the source file and did a search and replace of the #define name with its expansion.

For example:

define SOME_DEFINE 123.45

... int y = SOME_DEFINE;

The c pre-processor parses your source file, and anywhere it finds "SOME_DEFINE" it replaces it with "123.45". It also removes ALL comments, and does many other things. When it's done, it passes that modified copy of your source file to the compiler. The compiler never sees "SOME_DEFINE", but only what it was replaced with. So, for the above line, it will see only:

int y = 123.45;

which WILL be type-checked, just like any other assignment.

A #define can be somewhat ambiguous at times. Consider this (contrived) example:

define DEFINEVAL = 10

constant long longval = 10; ... int result = (10000 * DEFINEVAL) / 5000; int result = (10000 * longval) / 5000;

Those two lines will give very different results, because the first will use integer arithmetic, and overflow, while the second will use long arithmetic, and give the correct result.

The advantage of const is it enforces the read-only behavior, and it's type is always absolutely unambiguous.

Regards, Ray L.

@pYro, Ray: What I mean by typeless is that the macro allows any array type to be used as the argument in the macro.

Edit:

@Ray: In your example, I don't think you meant to put the assignment operator in the macro. What I was trying to say was that the "10" from the preprocessor pass in the statement:

int result = (10000 * DEFINEVAL) / 5000;

is not typechecked until it is used in resolution of the expression. Should the following fragment throw an error or not?

byte array[260]; byte count;

count = ELEMENTCOUNT(array);

In fact, should your example:

const long longval = 10; // I'm assuming you meant const

int result = (10000 * longval) / 5000;

throw an error?

econjack: Should the following fragment throw an error or not?

byte array[260]; byte count;

count = ELEMENTCOUNT(array);

Without knowing the definition of ELEMENTCOUNT, it's impossible to say...

econjack: In fact, should your example:

const long longval = 10; // I'm assuming you meant const

int result = (10000 * longval) / 5000;

throw an error?

No. Why should it? The syntax is perfectly legal, and functionally it will do exactly what one would expect.

Regards, Ray L.

@Ray:

Without knowing the definition of ELEMENTCOUNT, it's impossible to say...

From my first post:

#define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0]))

econjack: @Ray: From my first post:

#define ELEMENTCOUNT(x) (sizeof(x) / sizeof(x[0]))

No, it should not throw an error. Syntactically, it's perfectly valid. Of course, it will also not give the right answer....

Regards, Ray L.

halfdome: But how does this work with classes, where I pass an argument during initialization and never touch that later?

I agree with pYro_65 in reply #7.

You can set up class constants in the constructor initialization list.