views:

7586

answers:

9

I want to have a class with a private static data member (a vector that contains all the characters a-z). In java or C#, I can just make a "static constructor" that will run before I make any instances of the class, and sets up the static data members of the class. It only gets run once (as the variables are read only and only need to be set once) and since it's a function of the class it can access its private members. I could add code in the constructor that checks to see if the vector is initialized, and initialize it if it's not, but that introduces many necessary checks and doesn't seem like the optimal solution to the problem.

The thought occurs to me that since the variables will be read only, they can just be public static const, so I can set them once outside the class, but once again, it seems sort of like an ugly hack.

Is it possible to have private static data members in a class if I don't want to initialize them in the instance constructor?

+10  A: 

In the .h file:

class MyClass {
private:
    static int myValue;
};

In the .cpp file:

#include "myclass.h"

int MyClass::myValue = 0;
Ant
This works fine for individual static members (regardless of type). The deficiency in comparison to static constructors is that you can't impose an *order* between the various static members. If you need to do that, see Earwicker's answer.
quark
A: 

You define static member variables similarly to the way you define member methods.

foo.h

class Foo
{
public:
    void bar();
private:
    static int count;
};

foo.cpp

#include "foo.h"

void Foo::bar()
{
    // method definition
}

int Foo::count = 0;
Nick Lewis
A: 

To initialize a static variable, you just do so inside of a source file. For example:

//Foo.h
class Foo
{
 private:
  static int hello;
};


//Foo.cpp
int Foo::hello = 1;
Cristián Romo
+20  A: 

To get the equivalent of a static constructor, you need to write a separate ordinary class to hold the static data and then make a static instance of that ordinary class.

class StaticStuff
{
     std::vector<char> letters_;

public:
     StaticStuff()
     {
         for (char c = 'a'; c <= 'z'; c++)
             letters_.push_back(c);
     }

     // provide some way to get at letters_
};

class Elsewhere
{
    static StaticStuff staticStuff; // constructor runs once, single instance

};
Daniel Earwicker
thanks! though that's very annoying to have to do all that. One of the many "mistakes" C# and java learned from.
CrazyJugglerDrummer
Yes. I always point out to people that if C++ hadn't made all those "mistakes" then other languages would have to make them. C++ covering so much ground, even making mistakes, has been great for the languages that followed it.
quark
Just one little nuance, as constructors come into play no one guarantees when the constructor for static object executes. A well-known much safer approach isclass Elsewhere{ StaticStuff // constructor runs once, when someone first needs it return staticStuff; }};I wonder if static constructors in C# and Java can provide the same guarantee as the code above...
Oleg Zhylin
@Oleg: Yes they do. The standard gurantees that the constructors for all non local variables are executed before main is entered. It also gurantees that within a compilation unit the order of construction is well defined and the same order as declaration within the compilation unit. Unfortunately they do not define the order across multiple compilation units.
Martin York
@Oleg: further to Martin's answer about order of construction in C++, the answer for C# is that code is dynamically loaded on demand, and statics are initialized on demand, and this is a thread-safe facility, so statics effectively work like thread-safe lazy singletons (with a built-in correct implementation of double-checked locking). Hence order-of-init problems can still occur where there are circular references, but they are unusual. Not sure how much this is true in Java.
Daniel Earwicker
As for language wars, it shouldn't be a surprise that Java has some nice things that C++ doesn't, as they were created a decade apart, and then C# came about five years after Java. This is surely not controversial. Also C++ and the other two languages don't even serve the same purpose, really.
Daniel Earwicker
@quark: well said, sir.
Randolpho
This is actually a case where `friend` makes a lot of sense so that class `Elsewhere` may easily access `StaticStuff`’s internals (without breaking encapsulation in any dangerous way, I might add).
Konrad Rudolph
+2  A: 

Well you can have

class MyClass
{
    public:
        static vector<char> a;
        static class _init
        {
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;
};
EFraim
+1 (didn't try it out) But: When is ctor _init._init() called? Before or after the ctor of MyClass when I have a static MyClass object? I guess you can't tell...
ur
hello,where can I find more about this "initializer" magic?
Karel Bílek
+4  A: 

The concept of static constructors was introduced in Java after they learned from the problems in C++. So we have no direct equivalent.

The best solution is to use POD types that can be initialised explicitly.
Or make your static members a specific type that has its own constructor that will initialize it correctly.

//header

class A
{
    // Make sure this is private so that nobody can missues the fact that
    // you are overriding std::vector. Just doing it here as a quicky example
    // don't take it as a recomendation for deriving from vector.
    class MyInitedVar: public std::vector<char>
    {
        public:
        MyInitedVar()
        {
           // Pre-Initialize the vector.
           for(char c = 'a';c <= 'z';++c)
           {
               push_back(c);
           }
        }
    };
    static int          count;
    static MyInitedVar  var1;

};


//source
int            A::count = 0;
A::MyInitedVar A::var1;
Martin York
+2  A: 

No need for an init() function, std::vector can be created from a range:

// h file:
class MyClass {
    static std::vector<char> alphabet;
// ...
};

// cpp file:
#include <boost/range.hpp>
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
std::vector<char> MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) );

Note, however, that statics of class type cause trouble in libraries, so they should be avoided there.

I like it. Though if only we could do it in one line without the now usless alphabet.
Martin York
A: 

How about creating a template to mimic the behavior of C#.

template<class T> class StaticConstructor
{
    bool m_StaticsInitialised = false;

public:
    typedef void (*StaticCallback)(void);

    StaticConstructor(StaticCallback callback)
    {
        if (m_StaticsInitialised)
            return;

        callback();

        m_StaticsInitialised = true;
    }
}

template<class T> bool StaticConstructor<T>::m_StaticsInitialised;

class Test : public StaticConstructor<Test>
{
    static std::vector<char> letters_;

    static void _Test()
    {
        for (char c = 'a'; c <= 'z'; c++)
            letters_.push_back(c);
    }

public:
    Test() : StaticConstructor<Test>(&_Test)
    {
        // non static stuff
    };
};
karmasponge
A: 

When trying to compile and use class Elsewhere (from Earwicker's answer) I get:

error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)

It seems is not possible to initialize static attributes of non-integer types without putting some code outside the class definition (CPP).

To make that compile you can use "a static method with a static local variable inside" instead. Something like this:

class Elsewhere
{
public:
    static StaticStuff& GetStaticStuff()
    {
        static StaticStuff staticStuff; // constructor runs once, single instance
        return staticStuff;
    }
};

And you may also pass arguments to the constructor or initialize it with specific values, it is very flexible, powerfull and easy to implement... the only thing is you have a static method containing a static variable, not a static attribute... syntaxis changes a bit, but still useful. Hope this is useful for someone,

Hugo González Castro.

Hugo González Castro