Szukaj…


Wprowadzenie

Specyfikatory klas pamięci to słowa kluczowe, które mogą być używane w deklaracjach. Nie wpływają na rodzaj deklaracji, ale zazwyczaj modyfikują sposób przechowywania encji.

Uwagi

Istnieje sześć specyfikatorów klas pamięci, choć nie wszystkie w tej samej wersji języka: auto (do C ++ 11), register (do C ++ 17), static , thread_local (od C ++ 11), extern i mutable .

Zgodnie ze standardem

Co najwyżej jeden specyfikator klasy pamięci powinien pojawić się w danym dekoderze-specyfikacji-seq, z tym wyjątkiem, że thread_local może pojawić się ze static lub extern .

Deklaracja może nie zawierać specyfikatora klasy pamięci. W takim przypadku język określa zachowanie domyślne. Na przykład domyślnie zmienna zadeklarowana w zakresie bloku domyślnie ma automatyczny czas przechowywania.

zmienny

Specyfikator, który można zastosować do deklaracji niestatycznego, nie referencyjnego elementu danych klasy. Zmienny element klasy nie jest const nawet gdy obiekt jest 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

Drugie znaczenie dla mutable dodać w C ++ 11. Gdy podąża za listą parametrów lambda, pomija ukrytą const operatora wywołania funkcji lambda. Dlatego zmienna lambda może modyfikować wartości jednostek przechwyconych przez kopię. Zobacz mutable lambdas po więcej szczegółów.

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

Zauważ, że mutable nie jest specyfikatorem klasy pamięci, jeśli zostanie użyty w ten sposób do utworzenia mutowalnej lambda.

zarejestrować

C ++ 17

Specyfikator klasy pamięci wskazujący kompilatorowi, że zmienna będzie intensywnie używana. Słowo „rejestr” jest związane z faktem, że kompilator może wybrać zapisanie takiej zmiennej w rejestrze procesora, aby można było uzyskać do niej dostęp w mniejszej liczbie cykli zegara. Był przestarzały, począwszy od C ++ 11.

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

Zarówno zmienne lokalne, jak i parametry funkcji mogą być deklarowane jako register . W przeciwieństwie do C, C ++ nie nakłada żadnych ograniczeń na to, co można zrobić za pomocą zmiennej register . Na przykład poprawne jest pobranie adresu zmiennej register , ale może to uniemożliwić kompilatorowi przechowywanie takiej zmiennej w rejestrze.

C ++ 17

register słów kluczowych jest nieużywany i zarezerwowany. Program korzystający z register słów kluczowych jest źle sformułowany.

statyczny

Specyfikator klasy przechowywania static ma trzy różne znaczenia.

  1. Daje wewnętrzne powiązanie ze zmienną lub funkcją zadeklarowaną w zakresie przestrzeni nazw.

    // 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. Deklaruje, że zmienna ma statyczny czas przechowywania (chyba że jest to thread_local ). Zmienne zakresu przestrzeni nazw są domyślnie statyczne. Statyczna zmienna lokalna jest inicjowana tylko raz, po raz pierwszy kontrola przechodzi przez swoją definicję i nie jest niszczona przy każdym wyjściu z zakresu.

    void f() {
        static int count = 0;
        std::cout << "f has been called " << ++count << " times so far\n";
    }
    
  3. Po zastosowaniu do deklaracji członka klasy deklaruje ten element jako element statyczny .

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

Zauważ, że w przypadku statycznego elementu danych klasy, zarówno 2, jak i 3 stosuje się jednocześnie: słowo kluczowe static powoduje, że element staje się elementem danych statycznych i przekształca go w zmienną o statycznym czasie przechowywania.

automatyczny

C ++ 03

Deklaruje zmienną o automatycznym czasie przechowywania. Jest redundantny, ponieważ automatyczny czas przechowywania jest już domyślny w zakresie bloków, a specyfikator automatyczny nie jest dozwolony w zakresie przestrzeni nazw.

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

W C ++ 11 auto całkowicie zmieniło znaczenie i nie jest już specyfikatorem klasy pamięci, lecz jest używane do odliczenia typu .

zewnętrzny

Specyfikator klasy pamięci extern może modyfikować deklarację na jeden z trzech poniższych sposobów, w zależności od kontekstu:

  1. Można go użyć do zadeklarowania zmiennej bez jej definiowania. Zazwyczaj jest to używane w pliku nagłówkowym dla zmiennej, która zostanie zdefiniowana w osobnym pliku implementacji.

    // 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. Daje zewnętrzne powiązanie ze zmienną w zakresie przestrzeni nazw, nawet jeśli const lub constexpr przeciwnym razie spowodowałoby, że miałoby wewnętrzne powiązanie.

    // 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. Ponownie deklaruje zmienną o zasięgu bloku, jeśli została wcześniej zadeklarowana za pomocą powiązania. W przeciwnym razie deklaruje nową zmienną z powiązaniem, która jest członkiem najbliższej otaczającej przestrzeni nazw.

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

Funkcję można również zadeklarować jako extern , ale nie ma to żadnego efektu. Zwykle jest używana jako wskazówka dla czytelnika, że zadeklarowana tutaj funkcja jest zdefiniowana w innej jednostce tłumaczeniowej. Na przykład:

 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

W powyższym kodzie, gdyby f zmieniono na extern i g na non- extern , nie wpłynęłoby to wcale na poprawność lub semantykę programu, ale prawdopodobnie wprowadziłby w błąd czytelnika kodu.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow