Help with class static variables ?

Hi,

Can someone tell me why I have to re-declare class static variables, outside of the class as well as inside ??

See the line where I have to re-declare the class static variable testStaticVar outside of the class.

class MyClass 
{
public:
	static volatile bool testStaticVar;
	
	void TestStaticVariable();
};

void MyClass::TestStaticVariable()
{
MyClass::testStaticVar=false;
}

MyClass theClass;// Instantiate mn instance of MyClass

volatile bool MyClass::testStaticVar=false;//  ______IF YOU REMOVE THIS LINE IT NO LONGER COMPILES ___

void setup() {
  // put your setup code here, to run once:
theClass.TestStaticVariable();
}

void loop() {
  // put your main code here, to run repeatedly:

}

If you comment out that line, the code no longer links

I can understand needing to initialise static variables, but I can't understand the need to completely re-declare them in global scope

Can someone explain if I'm doing something wrong or if this is just the way C++ is ?

Thanks

Roger

A member with primitive type and constant static storage duration can be declared within a class.
Any other 'static' needs to be declared outside the class (should be in .cpp, not header).

It comes from the notion that it is not an instance variable, but a class variable. Also, as its effectively a global, you get to choose which .cpp/translation unit the definition belongs too.

A library could have multiple source files and only link one. Each file could have a different definition for the member depending on what tasks it does.

struct Foo{
** static const char something = 0x00; //Fine**
};

Sorry, I can’t make it a const, it needs to be a normal variable, as it gets accessed by a static method.

and static class methods can’t access instance variables unless you pass the static method an argument containing the instance, and its not possible in this case because my static method is being called as an ISR

Edit

Anyway. It sounds like this is a C++ quirk that I’m going to need to put up with.

rogerClark:
Sorry, I can’t make it a const, it needs to be a normal variable, as it gets accessed by a static method.

Const only denotes whether you can write to it, If the variable is static then a static function can access it directly (using my example above Foo::something), but yes the definition for said variable needs to be separated.

rogerClark:
Anyway. It sounds like this is a C++ quirk that I’m going to need to put up with.

Its not really a quirk, as I said above, you can decide where the variable is defined, but more importantly, the compiler cannot decide for you:

A static-member symbol is weakly defined, and a copy is generated every time the class is instantiated (in every source that uses it). Only the one with a definition is linked and used.

rogerClark:
I can understand needing to initialise static variables, but I can't understand the need to completely re-declare them in global scope

The answer is in the memory footprint created by an instance of a class.

class MyObject{
    uint8_t aByte;  //one byte of memory
    uint16_t aInt;  //two bytes of memory
};

MyObject anObject;  //create an instance of MyObject

When anObject was instanced, the compiler created a label in memory, with the name anObject. aByte was the first member and can be presumed to have a relative address of &0000. One byte is reserved for aByte, thus aInt has the relative address of &0001 and occupies two bytes;

class  MyObject{
  static byte aByte;
  int aInt;
};

byte MyObject::aByte = 10;  //there can be only one

MyObject anObject;
MyObject anotherObject;

The static declaration tells the compiler that aByte must always have the same address in memory. There may be multiple instances of the MyObject type but there can be only one instance of aByte. Thus, when the instances anObject and anotherObject are created, the aByte members are actually references (pointer), to the single instance of aByte. Hence, the relative address of the aInt member becomes &0002.

class MyClass {
  public:
    static uint8_t aByte;     //a reference to a byte,  which resolves to type uint8_t*
    uint16_t aInt;
    MyClass(void) {
      MyClass::aByte = 10;    //you can also initialise a static member in the constructor
    }
    
    void foo(void) {         //and methods are references too
      aByte++; 
    }
};

HTH

Hi Matt

OK. Thats kinda what I guessed.

Its just normally I program in other languages they hide what happening. ie in C# or Adobe's ActionScript 3 etc, if I create a static member in a class, I don't need to create another real variable to hold its data.
The compilor does it all for me, and I just seem to have a static var that is common to all instances of that class

But it looks like C++ is only doing half the job and not assigning some external memory, outside of the block of memory it must be allocating for the class object, to old the static var