How to initialize private static members in C++?

c++ initialization static-members

466912 просмотра

17 ответа

113385 Репутация автора

What is the best way to initialize a private, static data member in C++? I tried this in my header file, but it gives me weird linker errors:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

I'm guessing this is because I can't initialize a private member from outside the class. So what's the best way to do this?

Автор: Jason Baker Источник Размещён: 09.10.2008 03:34

Ответы (17)


519 плюса

207921 Репутация автора

Решение

The class declaration should be in the header file (Or in the source file if not shared).
File: foo.h

class foo
{
    private:
        static int i;
};

But the initialization should be in source file.
File: foo.cpp

int foo::i = 0;

If the initialization is in the header file then each file that includes the header file will have a definition of the static member. Thus during the link phase you will get linker errors as the code to initialize the variable will be defined in multiple source files.

Note: Matt Curtis: points out that C++ allows the simplification of the above if the static member variable is of const int type (e.g. int, bool, char). You can then declare and initialize the member variable directly inside the class declaration in the header file:

class foo
{
    private:
        static int const i = 42;
};
Автор: Martin York Размещён: 09.10.2008 03:36

86 плюса

20027 Репутация автора

For a variable:

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

This is because there can only be one instance of foo::i in your program. It's sort of the equivalent of extern int i in a header file and int i in a source file.

For a constant you can put the value straight in the class declaration:

class foo
{
private:
    static int i;
    const static int a = 42;
};
Автор: Matt Curtis Размещён: 09.10.2008 03:41

17 плюса

13588 Репутация автора

int foo::i = 0; 

Is the correct syntax for initializing the variable, but it must go in the source file (.cpp) rather than in the header.

Because it is a static variable the compiler needs to create only one copy of it. You have to have a line "int foo:i" some where in your code to tell the compiler where to put it otherwise you get a link error. If that is in a header you will get a copy in every file that includes the header, so get multiply defined symbol errors from the linker.

Автор: David Dibben Размещён: 09.10.2008 03:42

19 плюса

17947 Репутация автора

With a Microsoft compiler[1], static variables that are not int-like can also be defined in a header file, but outside of the class declaration, using the Microsoft specific __declspec(selectany).

class A
{
    static B b;
}

__declspec(selectany) A::b;

Note that I'm not saying this is good, I just say it can be done.

[1] These days, more compilers than MSC support __declspec(selectany) - at least gcc and clang. Maybe even more.

Автор: Johann Gerell Размещён: 09.10.2008 05:45

12 плюса

1488 Репутация автора

I don't have enough rep here to add this as a comment, but IMO it's good style to write your headers with #include guards anyway, which as noted by Paranaix a few hours ago would prevent a multiple-definition error. Unless you're already using a separate CPP file, it's not necessary to use one just to initialize static non-integral members.

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

I see no need to use a separate CPP file for this. Sure, you can, but there's no technical reason why you should have to.

Автор: monkey0506 Размещён: 07.01.2012 07:42

27 плюса

1232 Репутация автора

For future viewers of this question, I want to point out that you should avoid what monkey0506 is suggesting.

Header files are for declarations.

Header files get compiled once for every .cpp file that directly or indirectly #includes them, and code outside of any function is run at program initialization, before main().

By putting: foo::i = VALUE; into the header, foo:i will be assigned the value VALUE (whatever that is) for every .cpp file, and these assignments will happen in an indeterminate order (determined by the linker) before main() is run.

What if we #define VALUE to be a different number in one of our .cpp files? It will compile fine and we will have no way of knowing which one wins until we run the program.

Never put executed code into a header for the same reason that you never #include a .cpp file.

include guards (which I agree you should always use) protect you from something different: the same header being indirectly #included multiple times while compiling a single .cpp file

Автор: Joshua Clayton Размещён: 27.12.2012 03:37

5 плюса

0 Репутация автора

You can also include the assignment in the header file if you use header guards. I have used this technique for a C++ library I have created. Another way to achieve the same result is to use static methods. For example...

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

The above code has the "bonus" of not requiring a CPP/source file. Again, a method I use for my C++ libraries.

Автор: user2225284 Размещён: 11.04.2013 09:50

4 плюса

866 Репутация автора

I follow the idea from Karl. I like it and now I use it as well. I've changed a little bit the notation and add some functionality

#include <stdio.h>

class Foo
{
   public:

     int   GetMyStaticValue () const {  return MyStatic();  }
     int & GetMyStaticVar ()         {  return MyStatic();  }
     static bool isMyStatic (int & num) {  return & num == & MyStatic(); }

   private:

      static int & MyStatic ()
      {
         static int mStatic = 7;
         return mStatic;
      }
};

int main (int, char **)
{
   Foo obj;

   printf ("mystatic value %d\n", obj.GetMyStaticValue());
   obj.GetMyStaticVar () = 3;
   printf ("mystatic value %d\n", obj.GetMyStaticValue());

   int valMyS = obj.GetMyStaticVar ();
   int & iPtr1 = obj.GetMyStaticVar ();
   int & iPtr2 = valMyS;

   printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}

this outputs

mystatic value 7
mystatic value 3
is my static 1 0
Автор: Alejadro Xalabarder Размещён: 31.12.2013 02:45

3 плюса

2112 Репутация автора

Also working in privateStatic.cpp file :

#include <iostream>

using namespace std;

class A
{
private:
  static int v;
};

int A::v = 10; // possible initializing

int main()
{
A a;
//cout << A::v << endl; // no access because of private scope
return 0;
}

// g++ privateStatic.cpp -o privateStatic && ./privateStatic
Автор: andrew Размещён: 26.01.2014 11:21

3 плюса

63 Репутация автора

What about a set_default() method?

class foo
{
    public:
        static void set_default(int);
    private:
        static int i;
};

void foo::set_default(int x) {
    i = x;
}

We would only have to use the set_default(int x) method and our static variable would be initialized.

This would not be in disagreement with the rest of the comments, actually it follows the same principle of initializing the variable in a global scope, but by using this method we make it explicit (and easy to see-understand) instead of having the definition of the variable hanging there.

Автор: Arturo Ruiz Mañas Размещён: 23.05.2014 02:28

12 плюса

141 Репутация автора

If you want to initialize some compound type (f.e. string) you can do something like that:

class SomeClass {
  static std::list<string> _list;

  public:
    static const std::list<string>& getList() {
      struct Initializer {
         Initializer() {
           // Here you may want to put mutex
           _list.push_back("FIRST");
           _list.push_back("SECOND");
           ....
         }
      }
      static Initializer ListInitializationGuard;
      return _list;
    }
};

As the ListInitializationGuard is a static variable inside SomeClass::getList() method it will be constructed only once, which means that constructor is called once. This will initialize _list variable to value you need. Any subsequent call to getList will simply return already initialized _list object.

Of course you have to access _list object always by calling getList() method.

Автор: Kris Kwiatkowski Размещён: 23.11.2014 11:43

0 плюса

150 Репутация автора

Does this serves your purpose?

//header file

struct MyStruct {
public:
    const std::unordered_map<std::string, uint32_t> str_to_int{
        { "a", 1 },
        { "b", 2 },
        ...
        { "z", 26 }
    };
    const std::unordered_map<int , std::string> int_to_str{
        { 1, "a" },
        { 2, "b" },
        ...
        { 26, "z" }
    };
    std::string some_string = "justanotherstring";  
    uint32_t some_int = 42;

    static MyStruct & Singleton() {
        static MyStruct instance;
        return instance;
    }
private:
    MyStruct() {};
};

//Usage in cpp file
int main(){
    std::cout<<MyStruct::Singleton().some_string<<std::endl;
    std::cout<<MyStruct::Singleton().some_int<<std::endl;
    return 0;
}
Автор: David Nogueira Размещён: 02.09.2016 11:50

1 плюс

116 Репутация автора

I just wanted to mention something a little strange to me when I first encountered this.

I needed to initialize a private static data member in a template class.

in the .h or .hpp, it looks something like this to initialize a static data member of a template class:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;
Автор: Tyler Heers Размещён: 01.10.2016 01:11

27 плюса

8019 Репутация автора

Since C++17, static members may be defined in the header with the inline keyword.

http://en.cppreference.com/w/cpp/language/static

"A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify a default member initializer. It does not need an out-of-class definition:"

struct X
{
    inline static int n = 1;
};
Автор: Die in Sente Размещён: 12.07.2017 03:34

2 плюса

605 Репутация автора

The linker problem you encountered is probably caused by:

  • Providing both class and static member definition in header file,
  • Including this header in two or more source files.

This is a common problem for those who starts with C++. Static class member must be initialized in single translation unit i.e. in single source file.

Unfortunately, the static class member must be initialized outside of the class body. This complicates writing header-only code, and, therefore, I am using quite different approach. You can provide your static object through static or non-static class function for example:

class Foo
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};
Автор: no one special Размещён: 10.09.2017 10:20

5 плюса

174070 Репутация автора

Static constructor pattern that works for multiple objects

One idiom was proposed at: https://stackoverflow.com/a/27088552/895245 but here goes a cleaner version that does not require creating a new method per member.

main.cpp

#include <cassert>
#include <vector>

// Normally on the .hpp file.
class MyClass {
public:
    static std::vector<int> v, v2;
    static struct StaticConstructor {
        StaticConstructor() {
            v.push_back(1);
            v.push_back(2);
            v2.push_back(3);
            v2.push_back(4);
        }
    } _staticConstructor;
};

// Normally on the .cpp file.
std::vector<int> MyClass::v;
std::vector<int> MyClass::v2;
// Must come after every static member.
MyClass::StaticConstructor MyClass::_staticConstructor;

int main() {
    assert(MyClass::v[0] == 1);
    assert(MyClass::v[1] == 2);
    assert(MyClass::v2[0] == 3);
    assert(MyClass::v2[1] == 4);
}

GitHub upstream.

Compile and run:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

See also: static constructors in C++? I need to initialize private static objects

Tested on Ubuntu 19.04.

Автор: Ciro Santilli 新疆改造中心法轮功六四事件 Размещён: 19.01.2018 08:55

2 плюса

18235 Репутация автора

One "old-school" way to define constants is to replace them by a enum:

class foo
{
    private:
        enum {i = 0}; // default type = int
        enum: int64_t {HUGE = 1000000000000}; // may specify another type
};

This way doesn't require providing a definition, and avoids making the constant lvalue, which can save you some headaches, e.g. when you accidentally ODR-use it.

Автор: anatolyg Размещён: 13.06.2018 09:27
Вопросы из категории :
32x32