Поиск…


Вступление

Спецификаторы класса хранения - это ключевые слова, которые могут использоваться в объявлениях. Они не влияют на тип объявления, но обычно изменяют способ хранения объекта.

замечания

Существует шесть спецификаций класса хранения, хотя и не все в одной и той же версии языка: auto (до C ++ 11), register (до C ++ 17), static , thread_local (начиная с C ++ 11), extern и mutable .

Согласно стандарту,

Не более одного спецификатора класса хранения должно появляться в заданном spec-spec-seq, за исключением того, что thread_local может отображаться со static или extern .

Объявление может содержать спецификатор класса хранения. В этом случае язык определяет поведение по умолчанию. Например, по умолчанию переменная, объявленная в области блока, неявно имеет автоматическую продолжительность хранения.

изменчивый

Спецификатор, который может быть применен к объявлению нестатического, не ссылочного элемента данных класса. Изменчивый член класса не const даже если объект 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

Второе значение для mutable было добавлено в C ++ 11. Когда он следует за списком параметров лямбда, он подавляет неявный const в операторе вызова функции лямбда. Следовательно, изменяемая лямбда может изменять значения объектов, захваченных копией. См. Изменчивые лямбды для более подробной информации.

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

Обратите внимание, что mutable не является спецификатором класса хранения, когда используется таким образом, чтобы сформировать изменяемую лямбду.

регистр

C ++ 17

Спецификатор класса хранения, который подсказывает компилятору, что переменная будет сильно использоваться. Слово «регистр» связано с тем, что компилятор может захотеть сохранить такую ​​переменную в регистре CPU, чтобы к ней можно было получить доступ за меньшее количество тактов. Он устарел, начиная с C ++ 11.

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

Локальные переменные и параметры функции могут быть объявлены register . В отличие от C, C ++ не устанавливает никаких ограничений на то, что можно сделать с помощью переменной register . Например, можно принять адрес переменной register , но это может помешать компилятору фактически сохранить такую ​​переменную в регистре.

C ++ 17

register ключевых слов не используется и сохраняется. Программа, использующая register ключевых слов, плохо сформирована.

статический

Спецификатор класса static хранилища имеет три разных значения.

  1. Дает внутреннюю связь с переменной или функцией, объявленной в области пространства имен.

    // 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. Объявляет переменную для статической продолжительности хранения (если она не является thread_local ). Переменные пространства-пространства неявно статичны. Статическая локальная переменная инициализируется только один раз, первый контроль времени проходит через ее определение и не уничтожается при каждом выходе из нее.

    void f() {
        static int count = 0;
        std::cout << "f has been called " << ++count << " times so far\n";
    }
    
  3. При применении к объявлению члена класса объявляет этот элемент статическим членом .

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

Обратите внимание, что в случае статического члена данных класса оба одновременно применяются как 2, так и 3: static ключевое слово делает его членом в статическом элементе данных и превращает его в переменную со статической продолжительностью хранения.

авто

C ++ 03

Объявляет переменную для автоматического хранения. Это избыточно, поскольку автоматическая длительность хранения уже является значением по умолчанию в области блока, а авто-спецификатор не разрешен в области пространства имен.

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

В C ++ 11 auto изменяется смысл и больше не является спецификатором класса хранения, а вместо этого используется для вывода типа .

внешний

Спецификатор класса extern хранилища может изменять объявление одним из трех следующих способов, в зависимости от контекста:

  1. Его можно использовать для объявления переменной без ее определения. Как правило, это используется в файле заголовка для переменной, которая будет определена в отдельном файле реализации.

    // 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. Это дает внешнюю связь с переменной в пространстве пространства имен, даже если const или constexpr иначе вызвали бы ее внутреннюю связь.

    // 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. Он переопределяет переменную в области блока, если она была ранее объявлена ​​с помощью связи. В противном случае он объявляет новую переменную с привязкой, которая является членом ближайшего охватывающего пространства имен.

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

Функция также может быть объявлена extern , но это не имеет никакого эффекта. Он обычно используется как подсказка для читателя о том, что заявленная здесь функция определена в другой единицы перевода. Например:

 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

В приведенном выше коде, если f был изменен на extern и g на non extern , это никак не повлияет на правильность или семантику программы, но скорее всего путает читателя кода.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow