¿Por qué usar prefijos como m_ en miembros de datos en clases de C++?

11 minutos de lectura

Avatar de usuario de VoidPointer
VoidPointer

Una gran cantidad de código C++ usa convenciones sintácticas para marcar miembros de datos. Los ejemplos comunes incluyen

  • m_memberName para miembros públicos (donde los miembros públicos se usan en absoluto)
  • _memberName para miembros privados o todos los miembros

Otros tratan de hacer cumplir usando this->member cada vez que se utiliza un miembro de datos.

En mi experiencia, la mayoría de las bases de código más grandes fallan al aplicar tales reglas de manera consistente.

En otros idiomas, estas convenciones están mucho menos extendidas. Lo veo solo ocasionalmente en código Java o C#. Creo que nunca lo he visto en código Ruby o Python. Por lo tanto, parece haber una tendencia con lenguajes más modernos a no usar marcas especiales para miembros de datos.

¿Esta convención sigue siendo útil hoy en día en C++ o es solo un anacronismo, especialmente porque se usa de manera tan inconsistente en las bibliotecas? ¿No han demostrado los otros idiomas que uno puede prescindir de los prefijos de los miembros?

  • Lo prefiero; en bases de código complejas, puede ser importante saber qué vars son locales y cuáles no. Generalmente uso el prefijo en lugar de forzar esto->, lo que me parece que es mucho tipeo adicional y opcional (mientras que nombrar lo obligará a hacerlo)

    – José

    4 de agosto de 2009 a las 15:35

  • Nunca lo ha visto en Ruby debido a @ para el atributo y la expresión idiomática de generar accesores en lugar de usar los atributos directamente.

    –Steve Jessop

    4 de agosto de 2009 a las 17:17

  • De acuerdo a PEP 8 las variables miembro no públicas deben tener un prefijo con un guión bajo en Python (ejemplo: self._something = 1).

    –Nathan Osman

    2 de abril de 2013 a las 1:52


  • ¿No debería usarse el resaltado de sintaxis del editor para identificarlos?

    – también

    14 de julio de 2015 a las 14:49

  • Has visto el equivalente de this->member en código Python. En Python normalmente sería self.member y no es sólo una convención, es requerida por el lenguaje.

    – matec

    14 de mayo de 2016 a las 8:23

Generalmente no uso un prefijo para las variables miembro.

Solía ​​usar un m prefijo, hasta que alguien señaló que “C++ ya tiene un prefijo estándar para el acceso de miembros: this->.

Así que eso es lo que uso ahora. Eso es, cuando hay ambiguedadagrego el this-> prefijo, pero por lo general, no existe ambigüedad, y puedo referirme directamente al nombre de la variable.

Para mí, eso es lo mejor de ambos mundos. Tengo un prefijo que puedo usar cuando lo necesito y soy libre de omitirlo siempre que sea posible.

Por supuesto, la respuesta obvia a esto es “sí, pero entonces no se puede ver de un vistazo si una variable es miembro de una clase o no”.

A lo que digo “¿y qué? Si necesita saber eso, su clase probablemente tenga demasiado estado. O la función es demasiado grande y complicada”.

En la práctica, he encontrado que esto funciona extremadamente bien. Como beneficio adicional, me permite promocionar una variable local a un miembro de la clase (o al revés) fácilmente, sin tener que cambiarle el nombre.

Y lo mejor de todo, ¡es consistente! No tengo que hacer nada especial ni recordar ninguna convención para mantener la coherencia.


por cierto, tu no debería use guiones bajos iniciales para los miembros de su clase. Te acercas incómodamente a los nombres que están reservados por la implementación.

La norma reserva todos los nombres que comiencen con doble guión bajo o guión bajo seguido de mayúscula. También reserva todos los nombres que comienzan con un solo guión bajo en el espacio de nombres global.

Por lo tanto, un miembro de la clase con un guión bajo seguido de una letra minúscula es legal, pero tarde o temprano hará lo mismo con un identificador que comience con mayúscula, o de lo contrario romperá una de las reglas anteriores.

Por lo tanto, es más fácil evitar los guiones bajos iniciales. Use un guión bajo de sufijo, o un m_ o solo m prefijo si desea codificar el alcance en el nombre de la variable.

  • “Entonces, un miembro de la clase con un guión bajo seguido de una letra minúscula es legal, pero tarde o temprano hará lo mismo con un identificador que comience con mayúscula, o de lo contrario romperá una de las reglas anteriores”. — las variables de miembros de clase no están en el espacio de nombres global, por lo que un guión bajo inicial es seguro, independientemente de si va seguido de una letra minúscula o mayúscula.

    – mbarnett

    8 de septiembre de 2011 a las 6:22


  • @mbarnett: No, el guión bajo seguido de mayúsculas está reservado en generalno solo en el espacio de nombres global.

    – jalf

    8 de septiembre de 2011 a las 7:45

  • sorprendido de que el voto de esta respuesta sea menor que el del prefijo.

    – Marson Mao

    2 de septiembre de 2014 a las 8:47

  • Estoy de acuerdo con esta respuesta, solo usa this-> si necesita especificar que es una variable miembro, o no, eso también es bueno.

    – David

    10 de enero de 2017 a las 22:14


  • Además, no tiene que documentar su convención para dar su código a otras personas. Todos entienden lo que this-> medio.

    – Caducón

    21 de septiembre de 2017 a las 8:41

  • En realidad, tanto _foo como _l están reservados en el ámbito del espacio de nombres.

    luego

    4 de agosto de 2009 a las 15:36

  • Pero están bien como nombres de variables miembro. No prefijo guiones bajos, porque las reglas son demasiado confusas y me quemé en el pasado.

    – Juan

    4 de agosto de 2009 a las 15:40

  • Estas no son palabras reservadas. Son nombres reservados. Si fueran palabras reservadas, no podrías usarlas en absoluto. Debido a que son nombres reservados, puede usarlos, pero bajo su propio riesgo.

    – TonyK

    30 de noviembre de 2010 a las 21:27

Prefiero los guiones bajos postfijos, así:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};

  • Yo también. También doy a los accesores/mutadores el mismo nombre.

    – robo

    4 de agosto de 2009 a las 18:46

  • Interesante. Parece un poco feo al principio, pero puedo ver cómo puede ser beneficioso.

    – ya23

    16 de noviembre de 2009 a las 13:57

  • Yo diría que es mucho menos feo que:” mBar” o “m_bar”.

    – sidan

    21 de abril de 2015 a las 8:18

  • pero entonces tienes vector<int> v_; y escribiendo v_.push_back(5) es bastante feo también

    – avim

    29/04/2015 a las 21:24


  • Ese es el estilo de Google C++.

    – Solo yo0

    26 de noviembre de 2015 a las 5:01

Últimamente he tendido a preferir el prefijo m_ en lugar de no tener ningún prefijo, la razón no es tanto que sea importante marcar las variables miembro, sino que evita la ambigüedad, digamos que tiene un código como:

void set_foo(int foo) { foo = foo; }

Eso de causa no sirve, solo uno foo permitido. Así que tus opciones son:

  • this->foo = foo;

    No me gusta, ya que provoca el sombreado de parámetros, ya no se puede usar g++ -Wshadow advertencias, también es más largo escribir entonces m_. También te encuentras con conflictos de nombres entre variables y funciones cuando tienes un int foo; y un int foo();.

  • foo = foo_; o foo = arg_foo;

    He estado usando eso por un tiempo, pero hace que las listas de argumentos sean feas, la documentación no debería tener que ver con la falta de ambigüedad del nombre en la implementación. Aquí también existen conflictos de nombres entre variables y funciones.

  • m_foo = foo;

    La documentación de la API se mantiene limpia, no hay ambigüedad entre las funciones miembro y las variables y es más corto de escribir que this->. La única desventaja es que hace que las estructuras POD sean feas, pero como las estructuras POD no sufren la ambigüedad del nombre en primer lugar, no es necesario usarlo con ellas. Tener un prefijo único también facilita algunas operaciones de búsqueda y reemplazo.

  • foo_ = foo;

    La mayoría de las ventajas de m_ aplicar, pero lo rechazo por razones estéticas, un guión bajo al final o al principio solo hace que la variable se vea incompleta y desequilibrada. m_ simplemente se ve mejor. Usando m_ también es más extensible, ya que puedes usar g_ para globales y s_ por estática.

PD: La razón por la que no ves m_ en Python o Ruby es porque ambos lenguajes imponen su propio prefijo, Ruby usa @ para variables miembro y Python requiere self..

  • Yo también. También doy a los accesores/mutadores el mismo nombre.

    – robo

    4 de agosto de 2009 a las 18:46

  • Interesante. Parece un poco feo al principio, pero puedo ver cómo puede ser beneficioso.

    – ya23

    16 de noviembre de 2009 a las 13:57

  • Yo diría que es mucho menos feo que:” mBar” o “m_bar”.

    – sidan

    21 de abril de 2015 a las 8:18

  • pero entonces tienes vector<int> v_; y escribiendo v_.push_back(5) es bastante feo también

    – avim

    29/04/2015 a las 21:24


  • Ese es el estilo de Google C++.

    – Solo yo0

    26 de noviembre de 2015 a las 5:01

Avatar de usuario de OldPeculier
viejopeculier

Al leer una función miembro, saber quién es el “propietario” de cada variable es absolutamente esencial para comprender el significado de la variable. En una función como esta:

void Foo::bar( int apples )
{
    int bananas = apples + grapes;
    melons = grapes * bananas;
    spuds += melons;
}

…es bastante fácil ver de dónde vienen las manzanas y los plátanos, pero ¿qué pasa con las uvas, los melones y las papas? ¿Deberíamos buscar en el espacio de nombres global? ¿En la declaración de clase? ¿Es la variable un miembro de este objeto o un miembro de la clase de este objeto? Sin saber la respuesta a estas preguntas, no puedes entender el código. Y en una función más larga, incluso las declaraciones de variables locales como manzanas y plátanos pueden perderse en la confusión.

Anteponer una etiqueta consistente para variables globales, variables miembro y variables miembro estáticas (quizás g_, m_ y s_ respectivamente) aclara la situación al instante.

void Foo::bar( int apples )
{
    int bananas = apples + g_grapes;
    m_melons = g_grapes * bananas;
    s_spuds += m_melons;
}

Al principio, puede llevar un tiempo acostumbrarse, pero luego, ¿qué en la programación no lo hace? Hubo un día en que incluso { y } te parecieron raros. Y una vez que te acostumbras a ellos, te ayudan a entender el código mucho más rápido.

(Usar “this->” en lugar de m_ tiene sentido, pero es aún más prolijo y visualmente disruptivo. No lo veo como una buena alternativa para marcar todos los usos de las variables miembro).

Una posible objeción al argumento anterior sería extender el argumento a los tipos. También podría ser cierto que conocer el tipo de una variable “es absolutamente esencial para comprender el significado de la variable”. Si es así, ¿por qué no agregar un prefijo a cada nombre de variable que identifique su tipo? Con esa lógica, terminas con la notación húngara. Pero mucha gente encuentra la notación húngara laboriosa, fea e inútil.

void Foo::bar( int iApples )
{
    int iBananas = iApples + g_fGrapes;
    m_fMelons = g_fGrapes * iBananas;
    s_dSpuds += m_fMelons;
}

húngaro hace cuéntanos algo nuevo sobre el código. Ahora entendemos que hay varias conversiones implícitas en la función Foo::bar(). El problema con el código ahora es que el valor de la información agregada por los prefijos húngaros es pequeño en relación con el costo visual. El sistema de tipos de C++ incluye muchas características para ayudar a que los tipos funcionen bien juntos o para generar una advertencia o un error del compilador. El compilador nos ayuda a tratar con tipos, no necesitamos notación para hacerlo. Podemos inferir fácilmente que las variables en Foo::bar() probablemente sean numéricas, y si eso es todo lo que sabemos, eso es lo suficientemente bueno para obtener una comprensión general de la función. Por lo tanto, el valor de conocer el tipo preciso de cada variable es relativamente bajo. Sin embargo, la fealdad de una variable como “s_dSpuds” (o incluso simplemente “dSpuds”) es genial. Por lo tanto, un análisis de costo-beneficio rechaza la notación húngara, mientras que el beneficio de g_, s_ y m_ supera el costo a los ojos de muchos programadores.

  • Gracias por la idea s_. Parece muy útil, y de alguna manera nunca se me había ocurrido.

    – Chris Olsen

    3 mayo 2020 a las 22:03

¿Ha sido útil esta solución?