ejemplo - constantes en c++




Uso de 'const' para parámetros de función. (20)

Si usa los operadores ->* o .* , Es una necesidad.

Te impide escribir algo como

void foo(Bar *p) { if (++p->*member > 0) { ... } }

que casi lo hice en este momento, y que probablemente no hace lo que pretendes.

Lo que pretendía decir era

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

y si hubiera puesto una const entre Bar * y p , el compilador me habría dicho eso.

¿Qué tan lejos vas con const ? ¿Hace funciones const cuando es necesario o va todo el cerdo y lo usa en todas partes? Por ejemplo, imagine un mutador simple que toma un único parámetro booleano:

void SetValue(const bool b) { my_val_ = b; }

¿Es esa const realmente útil? Personalmente opto por usarlo extensivamente, incluidos los parámetros, pero en este caso me pregunto si vale la pena.

También me sorprendió saber que puede omitir const de los parámetros en una declaración de función, pero puede incluirla en la definición de la función, por ejemplo:

archivo .h

void func(int n, long l);

archivo .cpp

void func(const int n, const long l)

¿Hay alguna razón para esto? Me parece un poco inusual.


A veces (con demasiada frecuencia) tengo que desenredar el código C ++ de otra persona. Y todos sabemos que el código C ++ de otra persona es un desastre completo casi por definición :) Por lo tanto, lo primero que hago para descifrar el flujo de datos local es poner const en cada definición de variable hasta que el compilador comienza a ladrar. Esto también implica argumentos de valor de calificación constante, ya que son solo variables locales sofisticadas iniciadas por el llamante.

Ah, me gustaría que las variables fueran const de forma predeterminada y mutable fuera necesaria para las variables no const :)


Cuando codifiqué C ++ para ganarme la vida, consteé todo lo que pude. Usar const es una excelente manera de ayudar al compilador a que te ayude. Por ejemplo, la constancia de los valores de retorno de su método puede salvarlo de errores tipográficos como:

foo() = 42

cuando quisiste decir

foo() == 42

Si foo () está definido para devolver una referencia no constante:

int& foo() { /* ... */ }

El compilador le permitirá asignar un valor al temporal anónimo devuelto por la llamada a la función. Haciéndolo const

const int& foo() { /* ... */ }

Elimina esta posibilidad.


En el caso que mencionas, no afecta a quienes llaman a tu API, por lo que no se hace comúnmente (y no es necesario en el encabezado). Solo afecta la implementación de tu función.

No es particularmente malo hacer esto, pero los beneficios no son tan buenos ya que no afectan su API y agregan escritura, por lo que generalmente no se hace.


La razón es que const para el parámetro solo se aplica localmente dentro de la función, ya que está trabajando en una copia de los datos. Esto significa que la firma de la función es realmente la misma de todos modos. Sin embargo, es probable que sea un estilo malo hacer esto mucho.

Personalmente tiendo a no usar const, excepto para los parámetros de referencia y puntero. Para los objetos copiados realmente no importa, aunque puede ser más seguro ya que señala la intención dentro de la función. Es realmente una llamada de juicio. Sin embargo, tiendo a usar const_iterator cuando hago bucles en algo y no pretendo modificarlo, así que supongo que cada uno es suyo, siempre que se mantenga rigurosamente la corrección constante para los tipos de referencia.


Las constantes superfluas son malas desde un punto de vista API:

Poner constantes superfluos en su código para los parámetros de tipo intrínseco pasados ​​por el valor desordena su API sin hacer una promesa significativa para el usuario que llama o para la API (solo obstaculiza la implementación).

Demasiado 'const' en una API cuando no se necesita es como " lobo llorando ", eventualmente la gente comenzará a ignorar 'const' porque está en todas partes y no significa nada la mayor parte del tiempo.

El argumento "reductio ad absurdum" a consts adicionales en API es bueno porque estos dos primeros puntos serían si más parámetros const son buenos, entonces cada argumento que pueda tener una const, DEBERÍA tener una const. De hecho, si fuera realmente tan bueno, querría que const fuera el valor predeterminado para los parámetros y tuviera una palabra clave como "mutable" solo cuando quiera cambiar el parámetro.

Así que intentemos poner const en donde podamos:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Considere la línea de código anterior. La declaración no solo es más abarrotada y más larga y más difícil de leer, sino que el usuario de la API puede ignorar con seguridad tres de las cuatro palabras clave 'const'. Sin embargo, el uso adicional de 'const' ha hecho que la segunda línea sea potencialmente PELIGROSA.

¿Por qué?

Una mala lectura rápida del primer parámetro char * const buffer puede hacerle pensar que no modificará la memoria en el buffer de datos que se pasa; sin embargo, esto no es cierto. El 'const' superfluo puede llevar a suposiciones peligrosas e incorrectas sobre su API cuando se escanea o se lee mal rápidamente.

Las constantes superfluas también son malas desde el punto de vista de la Implementación de Código:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Si FLEXIBLE_IMPLEMENTATION no es cierto, entonces la API "promete" no implementar la función de la primera manera a continuación.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Esa es una promesa muy tonta de hacer. ¿Por qué debería hacer una promesa que no beneficia en absoluto a su interlocutor y solo limita su implementación?

Sin embargo, ambas son implementaciones perfectamente válidas de la misma función, por lo que todo lo que has hecho está atado una mano a la espalda innecesariamente.

Además, es una promesa muy superficial que es fácil (y legalmente burlada).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Mira, lo implementé de todos modos, aunque prometí no hacerlo, solo usando una función de envoltura. Es como cuando el malo promete no matar a alguien en una película y le ordena a su secuaz que lo mate.

Esas constantes superfluas no valen más que una promesa de un malo de la película.

Pero la capacidad de mentir se pone aún peor:

Se me ha informado que puede no coincidir la constante en el encabezado (declaración) y el código (definición) utilizando constantes no esenciales. Los defensores de const-happy afirman que esto es algo bueno, ya que le permite poner const solo en la definición.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

Sin embargo, lo contrario es cierto ... puede poner una constancia falsa solo en la declaración e ignorarla en la definición. Esto solo hace que la constante superflua en una API sea más de una cosa terrible y una mentira horrible - vea este ejemplo:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Todo lo que realmente hace la constante superflua es hacer que el código del implementador sea menos legible al obligarlo a usar otra copia local o una función de envoltorio cuando quiere cambiar la variable o pasar la variable por una referencia no constante.

Mira este ejemplo. ¿Cuál es más legible? ¿Es obvio que la única razón para la variable adicional en la segunda función es porque algún diseñador de API lanzó una constante superflua?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Esperemos que hayamos aprendido algo aquí. Superfluous const es una irritación por la API, una molestia molesta, una promesa superficial y sin sentido, un obstáculo innecesario y, en ocasiones, conduce a errores muy peligrosos.


Marcando los parámetros de valor 'const' es definitivamente una cosa subjetiva.

Sin embargo, en realidad prefiero marcar los parámetros de valor const, como en su ejemplo.

void func(const int n, const long l) { /* ... */ }

Para mí, el valor indica claramente que la función nunca cambia los valores de los parámetros de la función. Tendrán el mismo valor al principio que al final. Para mí, es parte de mantener un estilo de programación muy funcional.

Para una función corta, podría decirse que es una pérdida de tiempo / espacio tener la 'const' allí, ya que generalmente es bastante obvio que la función no modifica los argumentos.

Sin embargo, para una función más grande, es una forma de documentación de implementación, y el compilador la aplica.

Puedo estar seguro de que si realizo algunos cálculos con 'n' y 'l', puedo refactorizar / mover ese cálculo sin temor a obtener un resultado diferente porque perdí un lugar donde uno o ambos cambian.

Dado que es un detalle de implementación, no necesita declarar los parámetros de valor const en el encabezado, al igual que no necesita declarar los parámetros de función con los mismos nombres que utiliza la implementación.


No pondría constantes en parámetros como ese: todos ya saben que un booleano (en lugar de un booleano &) es constante, por lo que agregarlo hará que la gente piense "espera, ¿qué?" O incluso que estás pasando el parámetro por referencia.


Para resumir:

  • "Normalmente, el paso por valor de const es inútil y, en el mejor de los casos, engañoso". Desde here
  • Pero puede agregarlos en el .cpp como lo haría con las variables.
  • Tenga en cuenta que la biblioteca estándar no utiliza const. Por ejemplo, std::vector::at(size_type pos) . Lo que es suficientemente bueno para la biblioteca estándar es bueno para mí.

Puede ser que este no sea un argumento válido. pero si incrementamos el valor de una variable const dentro de un compilador de funciones, nos daremos un error: " error: incremento del parámetro de solo lectura ". así que eso significa que podemos usar la palabra clave const como una forma de evitar la modificación accidental de nuestras variables dentro de las funciones (lo que se supone que no debemos / solo lectura). así que si lo hicimos accidentalmente en el compilador en tiempo de compilación nos lo haremos saber. Esto es especialmente importante si no eres el único que está trabajando en este proyecto.



Tiendo a usar const siempre que sea posible. (U otra palabra clave apropiada para el idioma de destino). Hago esto simplemente porque le permite al compilador realizar optimizaciones adicionales que de otra forma no podría realizar. Como no tengo idea de cuáles pueden ser estas optimizaciones, siempre lo hago, incluso cuando parece una tontería.

Por lo que sé, el compilador podría ver un parámetro de valor constante y decir: "Oye, esta función no lo está modificando de todos modos, así que puedo pasar por referencia y guardar algunos ciclos de reloj". No creo que alguna vez hiciera algo así, ya que cambia la firma de la función, pero hace el punto. Tal vez hace una manipulación de pila diferente o algo así ... El punto es, no lo sé, pero sé que tratar de ser más inteligente de lo que el compilador solo hace que me avergüencen.

C ++ tiene algo de equipaje adicional, con la idea de la corrección constante, por lo que se vuelve aún más importante.


Yo digo const sus parámetros de valor.

Considera esta función de buggy:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Si el parámetro número fuera const, el compilador se detendría y nos avisaría del error.


Yo uso const donde puedo. Const para parámetros significa que no deben cambiar su valor. Esto es especialmente valioso cuando se pasa por referencia. const para function declara que la función no debe cambiar los miembros de las clases.


const no tiene sentido cuando el argumento pasa por valor ya que no modificará el objeto de la persona que llama.

Se debe preferir const cuando se pasa por referencia, a menos que el propósito de la función sea modificar el valor pasado.

Finalmente, una función que no modifica el objeto actual (esto) puede, y probablemente debería ser declarada const. A continuación se muestra un ejemplo:

int SomeClass::GetValue() const {return m_internalValue;}

Esta es una promesa de no modificar el objeto al que se aplica esta llamada. En otras palabras, puedes llamar a:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Si la función no era constante, esto daría como resultado una advertencia del compilador.


"const no tiene sentido cuando el argumento se pasa por valor ya que no modificará el objeto de la persona que llama".

Incorrecto.

Se trata de auto-documentar su código y sus suposiciones.

Si su código tiene muchas personas trabajando en él y sus funciones no son triviales, debe marcar "const" cualquiera y todo lo que pueda. Al escribir un código de fuerza industrial, siempre debe asumir que sus compañeros de trabajo son psicópatas que intentan obtener lo que pueden (especialmente porque es usted mismo en el futuro).

Además, como alguien mencionó anteriormente, podría ayudar al compilador a optimizar un poco las cosas (aunque es una posibilidad remota).


Como los parámetros se pasan por valor, no hace ninguna diferencia si especifica const o no desde la perspectiva de la función que llama. Básicamente, no tiene ningún sentido declarar parámetros de paso por valor como const.


Lo que hay que recordar con const es que es mucho más fácil hacer que las cosas sean constantes desde el principio, que intentar ponerlas más tarde.

Use const cuando quiera que algo no cambie, es una sugerencia adicional que describe qué hace su función y qué esperar. He visto muchas API de C que podrían hacer con algunas de ellas, ¡especialmente las que aceptan cadenas en C!

Estaría más inclinado a omitir la palabra clave const en el archivo cpp que en el encabezado, pero como tiendo a cortarlos y pegarlos, se mantendrán en ambos lugares. No tengo idea de por qué el compilador lo permite, supongo que es una cosa del compilador. La mejor práctica es definitivamente poner tu palabra clave const en ambos archivos.


Sé que la pregunta está "un poco" desactualizada, pero cuando la encontré, alguien más también puede hacerlo en el futuro ... ... aún dudo que el pobre amigo se incluya aquí para leer mi comentario :)

Me parece que todavía estamos demasiado limitados a la forma de pensar de estilo C En el paradigma OOP jugamos con objetos, no con tipos. Const objeto puede ser conceptualmente diferente de un objeto no const, específicamente en el sentido de lógico-const (en contraste con bitwise-const). Por lo tanto, incluso si la corrección de la función params es (quizás) un exceso de cuidado en el caso de los POD, no lo es en el caso de los objetos. Si una función trabaja con un objeto const, debería decirlo. Considere el siguiente fragmento de código

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: puede argumentar que la referencia (const) sería más apropiada aquí y le da el mismo comportamiento. Bien a la derecha Solo dando una imagen diferente de lo que pude ver en otro lugar ...


El parámetro Const es útil solo cuando el parámetro se pasa por referencia, es decir, referencia o puntero. Cuando el compilador ve un parámetro const, se asegura de que la variable utilizada en el parámetro no se modifique dentro del cuerpo de la función. ¿Por qué alguien querría hacer un parámetro por valor como constante? :-)





const