ejemplos descargar definicion compiler caracteristicas c++

descargar - c++ manual



¿Debo devolver objetos const? (12)

En el artículo 03 Effective C++ , use const siempre que sea posible.

class Bigint { int _data[MAXLEN]; //... public: int& operator[](const int index) { return _data[index]; } const int operator[](const int index) const { return _data[index]; } //... };

const int operator[] hace la diferencia de int& operator[] .

Pero que pasa:

int foo() { }

y

const int foo() { }

Parece que son lo mismo.

Mi pregunta es, ¿por qué usamos const int operator[](const int index) const lugar de int operator[](const int index) const ?


Cuando se escribió ese libro, el consejo fue de poca utilidad, pero sirvió para evitar que el usuario escriba, por ejemplo, foo() = 42; y esperando que cambie algo persistente.

En el caso del operator[] , esto puede ser un poco confuso si no proporciona una sobrecarga sin const que devuelve una referencia no const , aunque tal vez pueda evitar esa confusión devolviendo una referencia const o un objeto proxy en su lugar de un valor

En estos días, es un mal consejo, ya que le impide vincular el resultado a una referencia de valor (no const ).

(Como se señaló en los comentarios, la pregunta es irrelevante cuando se devuelve un tipo primitivo como int , ya que el lenguaje impide que se asigne a un rvalue de ese tipo; me refiero al caso más general que incluye el retorno definido por el usuario tipos)


Debe distinguir claramente entre el uso constante que se aplica a los valores de retorno, los parámetros y la función en sí.

Valores devueltos

  • Si la función regresa por valor , la const no importa, ya que la copia del objeto se está devolviendo. Sin embargo, será importante en C ++ 11 con la semántica de movimiento involucrada.
  • Tampoco importa para los tipos básicos, ya que siempre se copian.

Considera la const std::string SomeMethod() const . No permitirá el uso de la función (std::string&&) , ya que espera valores no const. En otras palabras, la cadena devuelta siempre se copiará.

  • Si la función regresa por referencia , const protege el objeto devuelto para que no se modifique.

Parámetros

  • Si pasa un parámetro por valor , la const impide la modificación del valor dado por función en función . Los datos originales del parámetro no se pueden modificar de todos modos, ya que solo tiene copia.
  • Tenga en cuenta que dado que la copia siempre se crea, const solo tiene un significado para el cuerpo de la función , por lo tanto, solo se verifica en la definición de la función, no en la declaración (interfaz).
  • Si pasa un parámetro por referencia , se aplica la misma regla que en los valores devueltos

Función en sí

  • Si la función tiene const al final, solo puede ejecutar otras funciones const , y no puede modificar ni permitir la modificación de datos de clase. Por lo tanto, si regresa por referencia, la referencia devuelta debe ser const. Solo las funciones const pueden invocarse sobre el objeto o la referencia al objeto que es const. También los campos mutables se pueden cambiar.
  • El comportamiento creado por el compilador cambia this referencia a T const* . La función siempre puede const_cast this , pero por supuesto esto no se debe hacer y se considera inseguro.
  • Por supuesto, es sensato usar solo este especificador en los métodos de clase; las funciones globales con const al final generarán un error de compilación.

Conclusión

Si su método no modifica y nunca modificará las variables de clase, márquelo como const y asegúrese de cumplir con todas las críticas necesarias. Permitirá que se escriba un código más limpio, manteniéndolo así correcto . Sin embargo, poner const todas partes sin pensarlo ciertamente no es el camino a seguir.


El tipo de retorno const no es tan importante aquí. Como los int temporarios no son modificables, no hay diferencia observable en el uso de int vs const int . Vería una diferencia si utilizó un objeto más complejo que podría modificarse.


En el ejemplo del operador de indexación de matriz ( operator[] ), hace la diferencia.

Con

int& operator[](const int index) { /* ... */ }

puede usar la indexación para cambiar directamente la entrada en la matriz, por ejemplo, utilizándola así:

mybigint[3] = 5;

El segundo const int operator[](const int) se usa para recuperar el valor solamente.

Sin embargo, como valor de retorno de las funciones, para tipos simples como eg int no importa. Cuando importa es si está devolviendo tipos más complejos, digamos un std::vector , y no quiere que el llamante de la función modifique el vector.


Es posible que te pierdas el consejo de Meyers. La diferencia esencial es en el modificador const para el método.

Este es un método no const (note no const al final) lo que significa que está permitido modificar el estado de la clase.

int& operator[](const int index)

Este es un método const (aviso de const al final)

const int operator[](const int index) const

¿Qué pasa con los tipos del parámetro y el valor de retorno, hay una pequeña diferencia entre int y const int , pero no es relevante para el punto del consejo. Debería prestar atención a que la sobrecarga sin const devuelve int& que significa que puede asignarle, por ejemplo, num[i]=0 , y la sobrecarga const devuelve un valor no modificable (no importa si el tipo de retorno es int o const int ).

En mi opinión personal, si un objeto se pasa por valor , el modificador const es superfluo. Esta sintaxis es más corta y logra el mismo

int& operator[](int index); int operator[](int index) const;


Hay algunas buenas respuestas que giran en torno al tecnicismo de las dos versiones. Para un valor primitivo, no hace una diferencia.

Sin embargo , siempre he considerado que const esté allí para el programador en lugar del compilador. Cuando escribes const , estás diciendo explícitamente "esto no debería cambiar". Enfrentémoslo, const generalmente puede ser circunvolucrado, ¿verdad?

Cuando devuelve un const , le dice al programador que usa esa función que el valor no debería cambiar. Y si lo está cambiando, probablemente esté haciendo algo mal, porque él / ella no debería tener que hacerlo.

EDITAR: También creo que "usar const siempre que sea posible" es un mal consejo. Deberías usarlo donde tenga sentido.


La razón principal para devolver valores como const es que no se puede decir algo como foo() = 5; . Esto no es realmente un problema con los tipos primitivos, ya que no se puede asignar a valores r de tipos primitivos, pero es un problema con los tipos definidos por el usuario (como (a + b) = c; , con un operator+ sobrecargado operator+ ).

Siempre he encontrado la justificación para eso bastante endeble. No puede detener a alguien que intenta escribir código incómodo, y este tipo particular de coacción no tiene ningún beneficio real en mi opinión.

Con C ++ 11, en realidad hay una gran cantidad de daño que esta expresión idiomática está haciendo: la devolución de valores como const evita las optimizaciones de movimiento y, por lo tanto, debe evitarse siempre que sea posible. Básicamente, ahora consideraría esto un antipatrón.

Aquí hay un artículo relacionado tangencialmente sobre C ++ 11.


Mira eso:

const int operator[](const int index) const

la const en el final de la declaración. Describe que este método puede invocarse en constantes.

Por otro lado, cuando escribes solo

int& operator[](const int index)

solo se puede invocar en instancias no const y también proporciona:

big_int[0] = 10;

sintaxis.


No tiene mucho valor agregar calidades const valores const no-reference / non-pointer, y no tiene sentido agregarlo a los built-ins.

En el caso de los tipos definidos por el usuario , una calificación const evitará que las personas que llaman invoquen una función miembro no const en el objeto devuelto. Por ejemplo, dado

const std::string foo(); std::string bar();

entonces

foo().resize(42);

estaría prohibido, mientras

bar().resize(4711);

estaría permitido.

Para built-ins como int , esto no tiene ningún sentido en absoluto, ya que dichos valores no se pueden modificar de todos modos.

(Recuerdo que C ++ eficaz discutió haciendo que el tipo de retorno de operator=() una referencia const , y esto es algo a tener en cuenta).

Editar:

Parece que Scott realmente dio ese consejo . Si es así, entonces, debido a las razones dadas anteriormente, me resulta cuestionable incluso para C ++ 98 y C ++ 03 . Para C ++ 11, lo considero claramente incorrecto , como el propio Scott parece haber descubierto. En la errata para Effective C ++, 3rd ed. , él escribe (o cita a otros que se quejaron):

El texto implica que todas las devoluciones de valores por by deben ser const, pero los casos en que los valores de by-value no const son buenos diseños no son difíciles de encontrar, por ejemplo, tipos de devolución de std :: vector donde los llamantes usarán swap con un vector vacío para "agarrar" el contenido del valor de retorno sin copiarlos.

Y después:

Al declarar los valores de retorno de la función by-value, const evitará que se vinculen con referencias rvalue en C ++ 0x. Debido a que las referencias rvalue están diseñadas para ayudar a mejorar la eficiencia del código C ++, es importante tener en cuenta la interacción de los valores de retorno const y la inicialización de las referencias rvalue al especificar las firmas de funciones.


Para tipos primitivos (como int ), la const-ness del resultado no importa. Para las clases, podría cambiar el comportamiento. Por ejemplo, es posible que no pueda llamar a un método sin const en el resultado de su función:

class Bigint { const C foo() const { ... } ... } Bigint b; b.foo().bar();

Lo anterior está prohibido si bar() es una función constante de C En general, elija lo que tenga sentido.


Se ignoran los calificadores cv de nivel superior en los tipos de devolución de tipo no de clase. Lo que significa que incluso si escribes:

int const foo();

El tipo de devolución es int . Si el tipo de devolución es una referencia, por supuesto, la const ya no es de nivel superior, y la distinción entre:

int& operator[]( int index );

y

int const& operator[]( int index ) const;

es significante. (Tenga en cuenta también que en las declaraciones de funciones, como la anterior, cualquier cv-qualifiers de nivel superior también se ignoran).

La distinción también es relevante para los valores de devolución del tipo de clase: si devuelve T const , entonces la persona que llama no puede llamar a las funciones no const en el valor devuelto, por ejemplo:

class Test { public: void f(); void g() const; }; Test ff(); Test const gg(); ff().f(); // legal ff().g(); // legal gg().f(); // **illegal** gg().g(); // legal


Una de tus sobrecargas es devolver una referencia a un elemento en la matriz, que luego puedes cambiar.

int& operator[](const int index) { return _data[index]; }

La otra sobrecarga está devolviendo un valor para que la use.

const int operator[](const int index) const { return _data[index]; }

Ya que llama a cada una de estas sobrecargas de la misma manera, y nunca cambiará el valor cuando se usa.

int foo = myBigInt[1]; // Doesn''t change values inside the object. myBigInt[1] = 2; // Assigns a new value at index `