Frage

Was ist der beste Weg, um ein privates, statischen Daten Mitglied in C ++ zu initialisieren? Ich habe versucht, dies in meiner Header-Datei, aber es gibt mir seltsam Linkerfehler:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

Ich vermute, dies liegt daran, dass ich nicht über ein eigenes Mitglied von außerhalb der Klasse initialisieren. Also, was ist der beste Weg, dies zu tun?

War es hilfreich?

Lösung

Die Klassendeklaration in der Header-Datei sein sollte (oder in der Quelldatei, wenn nicht gemeinsam).
Datei: foo.h

class foo
{
    private:
        static int i;
};

Aber die Initialisierung sollte in Quelldatei sein.
Datei: foo.cpp

int foo::i = 0;

Wenn die Initialisierung in der Header-Datei ist dann jede Datei, die die Header-Datei enthält eine Definition des statischen Elements aufweist. So während der Verbindungsphase Sie Linker Fehler als Code erhalten wird die Variable zu initialisieren, wird in mehreren Quelldateien definiert werden.

Hinweis: Matt Curtis: weist darauf hin, dass C ++ die Vereinfachung der oben ermöglicht, wenn die statische Elementvariable const int-Typs ist (beispielsweise int, bool, char). Sie können dann erklären und die Membervariable direkt in der Klassendeklaration in der Header-Datei initialisiert werden:

class foo
{
    private:
        static int const i = 42;
};

Andere Tipps

Für ein Variable :

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

Das ist, weil es nur in Ihrem Programm eine Instanz foo::i sein kann. Es ist eine Art des Äquivalents von extern int i in einer Header-Datei und int i in einer Quelldatei.

Für ein Konstante Sie den Wert direkt in der Klassendeklaration setzen können:

class foo
{
private:
    static int i;
    const static int a = 42;
};

Für zukünftige Zuschauer dieser Frage möchte ich, dass Sie darauf hinweisen, sollten es vermeiden, was monkey0506 schlägt .

Header-Dateien sind für Erklärungen.

Header-Dateien für jede .cpp Datei kompiliert einmal bekommen, die sie direkt oder indirekt #includes und Code außerhalb jeder Funktion bei Programminitialisierung ausgeführt wird, bevor main().

Indem: foo::i = VALUE; in den Header wird foo:i den Wert VALUE zugeordnet wird (was auch immer das ist) für jede .cpp-Datei, und diese Zuordnungen in einer unbestimmten Reihenfolge (vom Linker bestimmt) passieren werden, bevor main() ausgeführt wird.

Was passiert, wenn wir eine andere Zahl in einem unserer #define VALUE Dateien sein .cpp? Es wird gut zusammenzustellen und wir nicht wissen, was man haben gewinnt, bis wir das Programm.

Nie ausgeführten Code in einen Header aus dem gleichen Grunde legen, die Sie nie eine #include Datei .cpp.

sind Wachen (die ich zustimmen sollten Sie immer verwenden) Sie etwas schützen anders: die gleiche Kopf indirekt mehrfach #included, während eine einzelne .cpp Datei zu kompilieren

Da C ++ 17, statische Elemente in der Kopfzeile können mit dem Inline Schlüsselwort definiert werden.

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

„Ein statisches Datenelement inline deklariert werden kann Inline-statisches Datenelement kann in der Klassendefinition definiert werden und einen Standard Elementinitialisierung angeben kann es nicht eine out-of-Class-Definition benötigt..“

struct X
{
    inline static int n = 1;
};

Mit einem Microsoft-Compiler [1], statische Variablen, die int artigen nicht auch in einer Header-Datei definiert werden, aber außerhalb der Klassendeklaration, den Microsoft spezifischen __declspec(selectany) verwendet wird.

class A
{
    static B b;
}

__declspec(selectany) A::b;

Beachten Sie, dass ich sage nicht, das ist gut, ich sage nur es kann getan werden.

[1] In diesen Tagen mehr als Compiler MSC Unterstützung __declspec(selectany) - zumindest gcc und Klirren. Vielleicht noch mehr.

int foo::i = 0; 

Ist die korrekte Syntax für die Variable zu initialisieren, aber es muss in der Quelldatei (CPP) gehen, anstatt in der Kopfzeile.

Da es sich um eine statische Variable ist der Compiler nur eine Kopie davon erstellen muss. Sie haben eine Linie haben „int foo: i“ einige, wo in Ihrem Code den Compiler zu sagen, wo es setzen sonst Sie einen Link Fehler. Wenn das in einem Header erhalten Sie eine Kopie in jeder Datei, die den Header enthält, so erhalten definierten Symbolfehler von dem Linker multiplizieren.

Ich habe hier nicht genug rep dies als Kommentar hinzuzufügen, aber IMO ist es gut, Stil Ihre Header mit # include Wachen sowieso, die wie vor von Paranaix ein paar Stunden bemerkt würde verhindern, dass ein Fehler multiple-Definition. Es sei denn, Sie bereits eine separate CPP-Datei verwenden, ist es nicht notwendig, eine verwenden nur statische Nicht-Integral-Mitglieder zu initialisieren.

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

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

Ich sehe keine Notwendigkeit, eine separate CPP-Datei für diesen Einsatz. Sicher, Sie können, aber es gibt keinen technischen Grund, warum Sie müssen sollte.

Wenn Sie einig Verbindungstyp (F. E. string) initialisieren man so etwas tun können:

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;
    }
};

als ListInitializationGuard eine statische Variable innerhalb SomeClass::getList() Verfahren ist, wird es nur einmal aufgebaut werden, was bedeutet, dass einmal Konstruktor aufgerufen wird. Dies wird Variable initialize _list Sie müssen schätzen. Jeder nachfolgende Aufruf von getList einfach zurückkehren bereits initialisiert _list Objekt.

Natürlich müssen Sie _list Objekt zugreifen immer durch getList() Methode aufrufen.

Sie können auch die Zuordnung in der Header-Datei enthalten, wenn Sie Kopfschutz verwenden. Ich habe diese Technik für eine C ++ verwendet Bibliothek, die ich erstellt habe. Ein anderer Weg, um das gleiche Ergebnis zu erzielen, ist statische Methoden zu verwenden. Zum Beispiel ...

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

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

Der obige Code hat den „Bonus“ von nicht CPP / Quelldatei erforderlich ist. Auch hier verwende eine Methode, die ich für meine C ++ Bibliotheken.

Ich folge der Idee von Karl. Ich mag es, und jetzt habe ich es auch nutzen. Ich habe ein wenig die Schreibweise geändert und einige Funktionen hinzufügen

#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));
}

Diese Ausgänge

mystatic value 7
mystatic value 3
is my static 1 0

Statische Konstruktor Muster, das für mehrere Objekte

funktioniert

Ein Idiom wurde vorgeschlagen, unter: https://stackoverflow.com/a/27088552/895245 aber hier geht ein sauberere Version, die nicht ein neues Verfahrens pro Mitglied und ein lauffähiges Beispiel der Erstellung erfordert:

#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 .

Siehe auch: statische Konstruktoren in C ++? Ich muss private statische Objekte initialisieren

Getestet mit g++ -std=c++11 -Wall -Wextra, GCC 7.3, Ubuntu 18.04.

Auch Arbeiten in privateStatic.cpp-Datei:

#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

Was ist mit einer set_default() Methode?

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

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

Wir würden nur die set_default(int x) Methode und unsere static Variable würde initialisiert verwenden.

Dies wäre nicht mit dem Rest der Kommentare in Widerspruch sein, tatsächlich folgt das gleiche Prinzip der Variablen in einem globalen Rahmen zu initialisieren, aber mit dieser Methode wir es explizit (und leicht zu sehen, verständlichen) machen statt mit der Definition der variablen hängen dort.

Das Linker Problem, das Sie wahrscheinlich angetroffen wird verursacht durch:

  • Die Bereitstellung sowohl Klasse und statische Elementdefinition in Header-Datei
  • Einschließlich diesen Header in zwei oder mehr Quelldateien.

Dies ist ein häufiges Problem für diejenigen, die mit C ++ beginnen. Statische Klasse Mitglied muss in einzelner Übersetzungseinheit das heißt in einzelner Quelldatei initialisiert werden.

Leider muss das statische Klassenmitglied außerhalb des Klassenkörpers initialisiert werden. Dies erschwert Kopf nur Code zu schreiben, und deshalb bin ich ganz anderen Ansatz. Sie können Ihr statisches Objekt durch statische oder nicht-statische Klasse Funktion zum Beispiel bieten:

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

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};

Ich wollte nur etwas ein wenig seltsam für mich erwähnen, wenn ich das erste anzutreffen.

Ich brauchte ein eigenes statisches Datenelement in einer Template-Klasse zu initialisieren.

in der .h oder .hpp, sieht es so etwas wie dieses ein statisches Datenelement einer Template-Klasse zu initialisieren:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;

Eine "old-school" Art und Weise Konstanten zu definieren, ist sie durch eine enum ersetzt werden:

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

Auf diese Weise ist es nicht erforderlich eine Definition, und vermeidet so dass die Konstante lvalue , die Ihnen einige Kopfschmerzen sparen , z.B wenn Sie versehentlich ODR-use es.

Ist dies dient Ihren Zweck?

//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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top