2009-01-18 19 views
110

Como programador novato en C++ hay algunas construcciones que aún me parecen muy poco claras, una de ellas es const. Puedes usarlo en muchos lugares y con tantos efectos diferentes que es casi imposible que un principiante salga con vida. ¿Algún gurú de C++ explicará una vez para siempre los diversos usos y si y por qué no usarlos?¿Cuántos y cuales son los usos de "const" en C++?

+0

buscando exactamente esa pregunta: D – alamin

Respuesta

94

Tratando de recoger algunos usos:

unión temporal alguna para hacer referencia a const, para alargar su vida útil. La referencia puede ser una base - y el destructor de la misma no tenga que ser virtuales - el destructor derecha todavía se llama:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction); 

Explicación, utilizando el código:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual 
}; 

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { } 
    ~Derived() { 
     t(); // call function 
    } 
}; 

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); } 

Este truco se utiliza en la clase de utilidad ScopeGuard de Alexandrescu. Una vez que el temporal se sale del alcance, el destructor de Derived se llama correctamente. El código anterior omite algunos pequeños detalles, pero ese es el gran problema.


Uso const para decirle a otros métodos no cambiará el estado lógico de este objeto.

struct SmartPtr { 
    int getCopies() const { return mCopiesMade; } 
}; 

Uso const para las clases de copia en escritura, para hacer que el compilador de ayuda a decidir cuándo y cuándo no es necesario copiar.

struct MyString { 
    char * getData() { /* copy: caller might write */ return mData; } 
    char const* getData() const { return mData; } 
}; 

Explicación: Es posible que desee compartir datos cuando se copia algo, siempre y cuando los datos del origen y el objeto copie'd siguen siendo los mismos. Una vez que uno de los objetos cambia los datos, usted necesita ahora dos versiones: una para el original y otra para la copia. Es decir, copia en un escribe para cualquier objeto, de modo que ahora ambos tengan su propia versión.

El uso de código:

int main() { 
    string const a = "1234"; 
    string const b = a; 
    // outputs the same address for COW strings 
    cout << (void*)&a[0] << ", " << (void*)&b[0]; 
} 

Las impresiones de los anteriores fragmentos de la misma dirección en mi GCC, porque la biblioteca se utiliza C++ implementa una copia en escritura std::string. Ambas cadenas, aunque son objetos distintos, comparten la misma memoria para sus datos de cadena. Hacer b no const preferirá la versión no const de operator[] y GCC creará una copia del buffer de memoria de respaldo, porque podríamos cambiarlo y no debe afectar los datos de a!

int main() { 
    string const a = "1234"; 
    string b = a; 
    // outputs different addresses! 
    cout << (void*)&a[0] << ", " << (void*)&b[0]; 
} 

Para el constructor de copia para hacer copias de objetos const y temporales:

struct MyClass { 
    MyClass(MyClass const& that) { /* make copy of that */ } 
}; 

Para hacer constantes que trivialmente no puede cambiar

double const PI = 3.1415; 

Para pasar objetos arbitrarios por referencia en lugar de por valor - para prevenir posiblemente caro o imposible por valor de pasar

void PrintIt(Object const& obj) { 
    // ... 
} 
+2

¿Puede explicar el primer y el tercer uso en sus ejemplos? – tunnuz

+0

"Para garantizar al destinatario que el parámetro no puede ser NULO" No veo cómo const tiene algo que ver con ese ejemplo. –

+0

¡Uy, así que no! de alguna manera comencé a escribir sobre referencias. muchas gracias por gemir :) por supuesto eliminaré eso ahora :) –

23

En realidad, hay 2 principales usos de const en C++.

Const valora

Si un valor es en forma de una variable, miembro, o parámetro que no (o no debe) ser alterado durante su vida útil debe marcarlo const. Esto ayuda a prevenir mutaciones en el objeto. Por ejemplo, en la siguiente función no necesito cambiar la instancia de Estudiante pasada, así que lo marqué const.

void PrintStudent(const Student& student) { 
    cout << student.GetName(); 
} 

En cuanto a por qué harías esto. Es mucho más fácil razonar acerca de un algoritmo si sabes que los datos subyacentes no pueden cambiar. "const" ayuda, pero no garantiza que esto se logre.

Obviamente, los datos de impresión a cout no exige mucho pensamiento :)

Marcado miembro de un método como const

En el ejemplo anterior he marcado estudiante como const. Pero, ¿cómo sabía C++ que llamar al método GetName() en el alumno no mutaría el objeto? La respuesta es que el método fue marcado como const.

class Student { 
    public: 
    string GetName() const { ... } 
}; 

Marcado de un método "const" hace 2 cosas. En primer lugar, le dice a C++ que este método no mutará mi objeto. Lo segundo es que todas las variables miembro se tratarán ahora como si estuvieran marcadas como const. Esto ayuda pero no evita que modifiques la instancia de tu clase.

Este es un ejemplo extremadamente simple pero espero que ayude a responder sus preguntas.

13

Tenga cuidado para entender la diferencia entre estos 4 declaraciones:

Las siguientes declaraciones son idénticos 2 semánticamente. Puede cambiar donde punto ccp1 y ccp2, pero no puede cambiar lo que están apuntando.

const char* ccp1; 
char const* ccp2; 

A continuación, el puntero es constante, así sea significativa debe ser inicializado para apuntar a algo.No puede hacer que apunte a otra cosa, sin embargo, lo que apunta a puede cambiar.

char* const cpc = &something_possibly_not_const; 

Por último, combinamos los dos - así que lo que se apunta a no puede ser modificado, y el puntero no podemos señalar a cualquier otro lugar.

regla espiral
const char* const ccpc = &const_obj; 

El agujas del reloj puede ayudar a desenredar una declaración http://c-faq.com/decl/spiral.anderson.html

+0

De una manera indirecta, sí lo hace! La regla espiral en el sentido de las agujas del reloj lo describe mejor: comience por el nombre (kpPointer) y dibuje una espiral en el sentido de las agujas del reloj que salga por el token, y diga cada token. Obviamente, no hay nada a la derecha de kpPointer, pero aún funciona. –

2

Como una pequeña nota, mientras leía here, es útil notar que

const se aplica a lo que está en su inmediata left (excepto si no hay nada allí en cuyo caso se aplica a lo que sea que sea su inmediato a la derecha).

Cuestiones relacionadas