Buscar..


Introducción

Los especificadores de clase de almacenamiento son palabras clave que se pueden usar en declaraciones. No afectan el tipo de la declaración, pero generalmente modifican la forma en que se almacena la entidad.

Observaciones

Hay seis especificadores de clase de almacenamiento, aunque no todos en la misma versión del idioma: auto (hasta C ++ 11), register (hasta C ++ 17), static , thread_local (desde C ++ 11), extern y mutable

Según la norma,

A lo sumo, un especificador de clase de almacenamiento aparecerá en un decl-specifier-seq dado , excepto que thread_local puede aparecer con static o extern .

Una declaración no puede contener ningún especificador de clase de almacenamiento. En ese caso, el idioma especifica un comportamiento predeterminado. Por ejemplo, de forma predeterminada, una variable declarada en el ámbito del bloque tiene implícitamente una duración de almacenamiento automático.

mudable

Un especificador que se puede aplicar a la declaración de un miembro de datos no estático y no de referencia de una clase. Un miembro mutable de una clase no es const incluso cuando el objeto es 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

Un segundo significado para mutable se añadió en C ++ 11. Cuando sigue la lista de parámetros de un lambda, suprime la const implícita en el operador de llamada de función del lambda. Por lo tanto, un lambda mutable puede modificar los valores de las entidades capturadas por copia. Ver lambdas mutable para más detalles.

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

Tenga en cuenta que mutable no es un especificador de clase de almacenamiento cuando se utiliza de esta manera para formar un lambda mutable.

registro

C ++ 17

Un especificador de clase de almacenamiento que insinúa al compilador que una variable será muy utilizada. La palabra "registro" está relacionada con el hecho de que un compilador puede elegir almacenar una variable de este tipo en un registro de CPU para que se pueda acceder a ella en menos ciclos de reloj. Fue desaprobado a partir de C ++ 11.

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

Tanto las variables locales como los parámetros de función pueden ser declarados de register . A diferencia de C, C ++ no impone restricciones a lo que se puede hacer con una variable de register . Por ejemplo, es válido tomar la dirección de una variable de register , pero esto puede impedir que el compilador realmente almacene dicha variable en un registro.

C ++ 17

El register palabras clave está sin uso y reservado. Un programa que utiliza el register palabras clave está mal formado.

estático

El especificador de clase de almacenamiento static tiene tres significados diferentes.

  1. Da enlace interno a una variable o función declarada en el ámbito del espacio de nombres.

    // 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. Declara que una variable tiene una duración de almacenamiento estática (a menos que sea thread_local ). Las variables del ámbito del espacio de nombres son implícitamente estáticas. Una variable local estática se inicializa solo una vez, el primer control de tiempo pasa por su definición y no se destruye cada vez que se sale de su alcance.

    void f() {
        static int count = 0;
        std::cout << "f has been called " << ++count << " times so far\n";
    }
    
  3. Cuando se aplica a la declaración de un miembro de la clase, declara que ese miembro es un miembro estático .

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

Tenga en cuenta que en el caso de un miembro de datos estáticos de una clase, tanto 2 como 3 se aplican simultáneamente: la palabra clave static convierte al miembro en un miembro de datos estáticos y lo convierte en una variable con una duración de almacenamiento estática.

auto

C ++ 03

Declara una variable que tiene duración de almacenamiento automático. Es redundante, ya que la duración del almacenamiento automático ya es la predeterminada en el ámbito del bloque, y el especificador automático no está permitido en el ámbito del espacio de nombres.

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

En C ++ 11, el significado de auto cambió completamente, y ya no es un especificador de clase de almacenamiento, sino que se usa para la deducción de tipos .

externo

El especificador de clase de almacenamiento extern puede modificar una declaración de una de las tres formas siguientes, según el contexto:

  1. Se puede utilizar para declarar una variable sin definirla. Por lo general, esto se usa en un archivo de encabezado para una variable que se definirá en un archivo de implementación separado.

    // 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. constexpr un enlace externo a una variable en el ámbito del espacio de nombres, incluso si const o constexpr hubieran provocado que tuviera un enlace interno.

    // 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. Redecierra una variable en el ámbito del bloque si se declaró previamente con un enlace. De lo contrario, declara una nueva variable con vinculación, que es un miembro del espacio de nombres envolvente más cercano.

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

Una función también puede ser declarada extern , pero esto no tiene efecto. Generalmente se usa como una sugerencia para el lector de que una función declarada aquí se define en otra unidad de traducción. Por ejemplo:

 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

En el código anterior, si f se cambiara a extern y g a no extern , no afectaría en absoluto la corrección o la semántica del programa, pero probablemente confundiría al lector del código.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow