enum ejemplos constantes c++ const

ejemplos - enum c++



¿Cuántos y cuáles son los usos de "const" en C++? (4)

Como programador novato en C ++, hay algunas construcciones que aún me parecen muy oscuras, 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?


Como una pequeña nota, cuando leo here , es útil notar que

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


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

Valores de Const

Si un valor tiene la forma de una variable, miembro o parámetro que no se modificará (o no debería) 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, imprimir datos para cout no requiere mucha reflexión :)

Marcado de un método miembro como const

En el ejemplo anterior marqué Student 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 { ... } };

Marcar 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.


Intentando recolectar algunos usos:

Enlazando algunos temporales a referencia-a-const, para alargar su tiempo de vida. La referencia puede ser una base, y el destructor no necesita ser virtual, el destructor correcto todavía se llama:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

Explicación , usando 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 usa 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.

Usar const para decirles a los demás que los métodos no cambiarán el estado lógico de este objeto.

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

Use const para las clases de copia por escritura , para que el compilador lo ayude a decidir cuándo y cuándo no necesita 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 copie algo, siempre que los datos del objeto original y del objeto copiado permanezcan iguales. 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 una escritura a cualquiera de los objetos, de modo que ahora ambos tienen su propia versión.

Usando el 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]; }

El fragmento anterior imprime la misma dirección en mi GCC, porque la biblioteca C ++ utilizada 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 del 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 que el constructor de copias haga copias de los objetos y temporales de const :

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

Para hacer constantes que trivialmente no pueden cambiar

double const PI = 3.1415;

Para pasar objetos arbitrarios por referencia en lugar de por valor , para evitar el paso de valores posiblemente costosos o imposibles

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


Tenga cuidado de comprender la diferencia entre estas 4 declaraciones:

Las siguientes 2 declaraciones son idénticas semánticamente. Puede cambiar dónde apuntan ccp1 y ccp2, pero no puede cambiar lo que apuntan.

const char* ccp1; char const* ccp2;

A continuación, el puntero es const, por lo que para ser significativo debe inicializarse para señalar algo. No puede hacer que apunte a otra cosa, sin embargo, lo que señala se puede cambiar.

char* const cpc = &something_possibly_not_const;

Finalmente, combinamos los dos, por lo que lo que se apunta no se puede modificar y el puntero no puede señalar a ningún otro lugar.

const char* const ccpc = &const_obj;

La regla espiral a la derecha puede ayudar a desenredar una declaración http://c-faq.com/decl/spiral.anderson.html