Suche…


Einführung

Speicherklassenspezifizierer sind Schlüsselwörter , die in Deklarationen verwendet werden können. Sie wirken sich nicht auf den Typ der Deklaration aus, ändern jedoch normalerweise die Art und Weise, in der die Entität gespeichert wird.

Bemerkungen

Es gibt sechs Speicherklassenspezifizierer, allerdings nicht alle in derselben Sprachversion: auto (bis C ++ 11), register (bis C ++ 17), static , thread_local (seit C ++ 11), extern und mutable .

Nach dem Standard

Es darf höchstens ein Speicherklassenspezifizierer in einem angegebenen thread_local -seq vorkommen, mit der Ausnahme, dass thread_local static oder extern .

Eine Deklaration darf keinen Speicherklassenspezifizierer enthalten. In diesem Fall gibt die Sprache ein Standardverhalten an. Standardmäßig hat eine im Blockbereich deklarierte Variable implizit eine automatische Speicherdauer.

veränderlich

Ein Bezeichner, der auf die Deklaration eines nicht statischen Nicht-Referenzdatenelements einer Klasse angewendet werden kann. Ein veränderlicher Member einer Klasse ist nicht const selbst wenn das Objekt const .

class C {
    int x;
    mutable int times_accessed;
  public:
    C(): x(0), times_accessed(0) {
    }
    int get_x() const {
        ++times_accessed; // ok: const member function can modify mutable data member
        return x;
    }
    void set_x(int x) {
        ++times_accessed;
        this->x = x;
    }
};
C ++ 11

Eine zweite Bedeutung für mutable wurde in C ++ 11 hinzugefügt. Wenn es der Parameterliste eines Lambda folgt, unterdrückt es die implizite const Anweisung des Funktionsaufrufoperators des Lambda. Daher kann ein veränderliches Lambda die Werte von durch Kopie erfassten Entitäten ändern. Weitere Informationen finden Sie in den veränderbaren Lambdas .

std::vector<int> my_iota(int start, int count) {
    std::vector<int> result(count);
    std::generate(result.begin(), result.end(),
                  [start]() mutable { return start++; });
    return result;
}

Beachten Sie, dass mutable kein Speicherklassenspezifizierer ist, wenn auf diese Weise ein mutierbares Lambda gebildet wird.

registrieren

C ++ 17

Ein Speicherklassenspezifizierer, der den Compiler darauf hinweist, dass eine Variable stark verwendet wird. Das Wort "Register" bezieht sich auf die Tatsache, dass ein Compiler eine solche Variable in einem CPU-Register speichern kann, so dass in weniger Taktzyklen darauf zugegriffen werden kann. Es wurde ab C ++ 11 veraltet.

register int i = 0;
while (i < 100) {
    f(i);
    int g = i*i;
    i += h(i, g);
}

Sowohl lokale Variablen als auch Funktionsparameter können als register deklariert register . Im Gegensatz zu C gibt C ++ keine Einschränkungen vor, was mit einer register ist. Es ist beispielsweise gültig, die Adresse einer register zu übernehmen, dies kann jedoch verhindern, dass der Compiler eine solche Variable tatsächlich in einem Register speichert.

C ++ 17

Das Schlüsselwort - register wird nicht verwendet und reserviert. Ein Programm, das das Schlüsselwortregister verwendet register ist schlecht geformt.

statisch

Der static Speicherklassenspezifizierer hat drei verschiedene Bedeutungen.

  1. Gibt eine interne Verknüpfung zu einer im Namespace-Bereich deklarierten Variablen oder Funktion.

    // internal function; can't be linked to
    static double semiperimeter(double a, double b, double c) {
        return (a + b + c)/2.0;
    }
    // exported to client
    double area(double a, double b, double c) {
        const double s = semiperimeter(a, b, c);
        return sqrt(s*(s-a)*(s-b)*(s-c));
    }
    
  2. Deklariert eine Variable mit statischer Speicherdauer (sofern es sich nicht um thread_local ). Namespace-Gültigkeitsbereichsvariablen sind implizit statisch. Eine statische lokale Variable wird nur einmal initialisiert. Das erste Mal, wenn die Steuerung ihre Definition durchläuft, wird nicht bei jedem Verlassen des Gültigkeitsbereichs zerstört.

    void f() {
        static int count = 0;
        std::cout << "f has been called " << ++count << " times so far\n";
    }
    
  3. Wenn es auf die Deklaration eines Klassenmitglieds angewendet wird, wird dieses Mitglied als statisches Mitglied deklariert.

    struct S {
        static S* create() {
            return new S;
        }
    };
    int main() {
        S* s = S::create();
    }
    

Beachten Sie, dass im Falle eines statischen Datenelements einer Klasse sowohl 2 als auch 3 gleichzeitig gelten: Das static Schlüsselwort macht das Member zu einem statischen Datenmitglied und zu einer Variablen mit statischer Speicherdauer.

Auto

C ++ 03

Deklariert eine Variable für die automatische Speicherdauer. Sie ist redundant, da die automatische Speicherdauer im Blockbereich bereits Standard ist und der automatische Bezeichner im Namespace-Bereich nicht zulässig ist.

void f() {
    auto int x; // equivalent to: int x;
    auto y;     // illegal in C++; legal in C89
}
auto int z;     // illegal: namespace-scope variable cannot be automatic

In C ++ 11 hat auto die Bedeutung komplett geändert und ist nicht länger ein Speicherklassenspezifizierer, sondern wird stattdessen für die Typabzugung verwendet .

extern

Der extern Speicherklassenspezifizierer kann eine Deklaration je nach Kontext auf eine der drei folgenden Arten ändern:

  1. Es kann verwendet werden, um eine Variable zu deklarieren, ohne sie zu definieren. Normalerweise wird dies in einer Headerdatei für eine Variable verwendet, die in einer separaten Implementierungsdatei definiert wird.

    // global scope
    int x;             // definition; x will be default-initialized
    extern int y;      // declaration; y is defined elsewhere, most likely another TU
    extern int z = 42; // definition; "extern" has no effect here (compiler may warn)
    
  2. Es gibt eine externe Verknüpfung zu einer Variablen im Gültigkeitsbereich des Namespaces, auch wenn const oder constexpr die interne Verknüpfung sonst verursacht hätte.

    // global scope
    const int w = 42;            // internal linkage in C++; external linkage in C
    static const int x = 42;     // internal linkage in both C++ and C
    extern const int y = 42;     // external linkage in both C++ and C
    namespace {
        extern const int z = 42; // however, this has internal linkage since
                                 // it's in an unnamed namespace
    }
    
  3. Es deklariert eine Variable im Blockbereich neu, wenn sie zuvor mit Verknüpfung deklariert wurde. Andernfalls wird eine neue Variable mit Verknüpfung deklariert, die dem nächstgelegenen umschließenden Namespace angehört.

    // global scope
    namespace {
        int x = 1;
        struct C {
            int x = 2;
            void f() {
                extern int x;           // redeclares namespace-scope x
                std::cout << x << '\n'; // therefore, this prints 1, not 2
            }
        };
    }
    void g() {
        extern int y; // y has external linkage; refers to global y defined elsewhere
    }
    

Eine Funktion kann auch extern deklariert werden, dies hat jedoch keine Auswirkungen. Sie wird normalerweise als Hinweis für den Leser verwendet, dass eine hier deklarierte Funktion in einer anderen Übersetzungseinheit definiert ist. Zum Beispiel:

 void f();        // typically a forward declaration; f defined later in this TU
 extern void g(); // typically not a forward declaration; g defined in another TU

Wenn im obigen Code f auf extern und g auf nicht extern geändert wurde, würde dies die Korrektheit oder Semantik des Programms nicht beeinträchtigen, würde aber den Leser des Codes wahrscheinlich verwirren.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow