636 Stimmen

Wie initialisiert man private statische Mitglieder in C++?

Wie lässt sich ein privates, statisches Datenelement in C++ am besten initialisieren? Ich habe dies in meiner Header-Datei versucht, aber es gibt mir seltsame Linker-Fehler:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

Ich vermute, dies ist, weil ich ein privates Mitglied von außerhalb der Klasse nicht initialisieren kann. Was ist also der beste Weg, dies zu tun?

19voto

David Dibben Punkte 17424
int foo::i = 0; 

ist die richtige Syntax für die Initialisierung der Variablen, aber sie muss in der Quelldatei (.cpp) und nicht im Header stehen.

Da es sich um eine statische Variable handelt, muss der Compiler nur eine Kopie davon erstellen. Sie müssen irgendwo in Ihrem Code die Zeile "int foo:i" einfügen, um dem Compiler mitzuteilen, wo er sie einfügen soll, sonst erhalten Sie einen Linkfehler. Wenn diese Zeile in einem Header steht, wird eine Kopie in jeder Datei erstellt, die den Header enthält, so dass der Linker Fehler bei mehrfach definierten Symbolen meldet.

16voto

Statisches C++11-Konstruktormuster, das für mehrere Objekte funktioniert

Eine Redewendung wurde bei vorgeschlagen: https://stackoverflow.com/a/27088552/895245 aber hier ist eine sauberere Version, die nicht die Erstellung einer neuen Methode pro Mitglied erfordert.

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

Kompilieren und ausführen:

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

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

Getestet auf Ubuntu 19.04.

C++17 Inline-Variable

Erwähnt unter: https://stackoverflow.com/a/45062055/895245 aber hier ist ein lauffähiges Beispiel für mehrere Dateien, um es noch deutlicher zu machen: Wie funktionieren Inline-Variablen?

Diese großartige C++17-Funktion ermöglicht es uns:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline constexpr int notmain_i = 42;

const int* notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}

Kompilieren und ausführen:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub vorgelagert .

15voto

Kris Kwiatkowski Punkte 331

Wenn Sie einen zusammengesetzten Typ (z.B. String) initialisieren wollen, können Sie so etwas tun:

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

Da die ListInitializationGuard ist eine statische Variable innerhalb von SomeClass::getList() Methode wird es nur einmal konstruiert, was bedeutet, dass der Konstruktor einmal aufgerufen wird. Dies wird initialize _list Variable auf den gewünschten Wert. Jeder nachfolgende Aufruf von getList wird einfach die bereits initialisierte _list Objekt.

Natürlich müssen Sie auf _list Objekt immer durch den Aufruf getList() método.

11voto

monkey0506 Punkte 2292

Ich habe hier nicht genug Ansehen, um dies als Kommentar hinzuzufügen, aber IMO ist es guter Stil, die Überschriften mit #include guards was, wie Paranaix vor ein paar Stunden feststellte, einen Fehler bei der Mehrfachdefinition verhindern würde. Sofern Sie nicht bereits eine separate CPP-Datei verwenden, ist es nicht notwendig, eine zu verwenden, nur um statische nicht-integrale 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, dafür eine separate CPP-Datei zu verwenden. Natürlich können Sie das, aber es gibt keinen technischen Grund, warum Sie das tun sollten.

7voto

no one special Punkte 1406

Das Linker-Problem, das bei Ihnen aufgetreten ist, hat wahrscheinlich folgende Ursachen:

  • Bereitstellung von Definitionen für Klassen und statische Mitglieder in der Header-Datei,
  • Einfügen dieses Headers in zwei oder mehr Quelldateien.

Dies ist ein häufiges Problem für diejenigen, die mit C++ beginnen. Statische Klassenmitglieder müssen in einer einzigen Übersetzungseinheit, d.h. in einer einzigen Quelldatei, initialisiert werden.

Leider muss das statische Klassenmitglied außerhalb des Klassenkörpers initialisiert werden. Dies erschwert das Schreiben von reinem Header-Code, und deshalb verwende ich einen ganz anderen Ansatz. Sie können Ihr statisches Objekt zum Beispiel durch eine statische oder nicht-statische Klassenfunktion bereitstellen:

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

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X