Uso excesivo de `this` en C ++

Estoy tratando con una gran base de código que usa la siguiente construcción a lo largo

class MyClass { public: void f(int x); private: int x; }; void MyClass::f(int x) { ' ' this->x = x; ' ' } 

Personalmente, siempre había usado y, por lo tanto, prefiero la forma

 class MyClass { public: void f(int x); private: int _x; }; void MyClass::f(int x) { ' ' _x = x; ' ' } 

Las razones por las que prefiero esto último son que es más sucinto (menos código = menos errores potenciales), y que no me gusta tener múltiples variables del mismo nombre en el scope al mismo tiempo, donde puedo evitarlo. Dicho esto, estoy viendo el uso anterior cada vez más a menudo en estos días. ¿Hay algún lado bueno para el segundo enfoque que desconozco? (por ejemplo, efecto en el tiempo de comstackción, uso con código de plantilla, etc.) ¿Las ventajas de cualquiera de los enfoques son lo suficientemente importantes como para que un refactor sea el otro? Expongo que, aunque no me gusta el segundo enfoque presente en el código, la cantidad de esfuerzo y el riesgo asociado de introducir más errores no merecen un refactor.

Su versión es un poco más limpia, pero mientras lo hace, yo:

  1. Evite el guion bajo: _x está bien hasta que alguien elija _MyField que es un nombre reservado. No se permite un guion bajo inicial seguido de una letra mayúscula como nombre de variable. Ver: ¿Cuáles son las reglas sobre el uso de un guión bajo en un identificador de C ++?
  2. Haga que el atributo sea privado o esté protegido: el cambio es seguro si se comstack y se asegurará de que se use su setter.
  3. La historia this-> tiene un uso, por ejemplo, en el código de plantilla para hacer que el nombre de campo dependa de tu tipo (puede resolver algunos problemas de búsqueda).

Un pequeño ejemplo de resoluciones de nombres que se corrigen utilizando un explícito this-> (probado con g ++ 3.4.3):

 #include  #include  class A { public: int g_; A() : g_(1) {} const char* f() { return __FUNCTION__; } }; const char* f() { return __FUNCTION__; } int g_ = -1; template < typename Base > struct Derived : public Base { void print_conflicts() { std::cout << f() << std::endl; // Calls ::f() std::cout << this->f() << std::endl; // Calls A::f() std::cout << g_ << std::endl; // Prints global g_ std::cout << this->g_ << std::endl; // Prints A::g_ } }; int main(int argc, char* argv[]) { Derived< A >().print_conflicts(); return EXIT_SUCCESS; } 

La denominación de campo no tiene nada que ver con un código. Como dijo Neil , la visibilidad del campo es el único código aquí.

Hay varios artículos sobre convenciones de nombres en C ++:

  • nombrando convención para variable pública y privada?
  • Convención de nomenclatura de métodos privados
  • el uso del espacio de nombres c ++ y las reglas de denominación

etc.

Este uso de ‘esto’ está recomendado por los estándares de encoding Microsoft C #. Proporciona una buena claridad de código, y pretende ser un estándar sobre el uso de m_ o _ o cualquier otra cosa en las variables miembro.

Honestamente, realmente no me gusta el guión bajo en los nombres de todos modos, solía marcar a todos mis miembros con una sola ‘m’.

Mucha gente usa esto porque en su IDE hará aparecer una lista de identificadores de la clase actual.

Sé que lo hago en el BCB.

Creo que el ejemplo que proporcionas con el conflicto de nombres es una excepción. Sin embargo, en Delphi, las pautas de estilo usan un prefijo (usualmente “a”) para los parámetros para evitar exactamente esto.

Mi opinión personal es que luchar contra una convención de encoding existente es algo que no debes hacer. Como Sutter / Alexandrescu lo pone en su libro ‘Convenciones de encoding C ++’: no ​​te preocupes por las cosas pequeñas. Cualquiera puede leer el uno o el otro, si hay un ‘this->’ o ‘_’ o lo que sea.

Sin embargo, la coherencia en las convenciones de nomenclatura es algo que normalmente desea, por lo que seguir una convención en algún ámbito (al menos el scope del archivo, idealmente la base de código completa, por supuesto) se considera una buena práctica. Usted mencionó que este estilo se usa a lo largo de una base de código más grande, por lo que creo que adaptar otra convención sería una mala idea.

Si, después de todo, encuentra que hay una buena razón para cambiarlo, no lo haga manualmente. En el mejor de los casos, su IDE admite este tipo de ‘refactorizaciones’. De lo contrario, escriba un script para cambiarlo. Buscar y reemplazar debería ser la última opción. En cualquier caso, debe tener una copia de seguridad (control de origen) y algún tipo de recurso de prueba automatizado. De lo contrario, no te divertirás con eso.

Usar ‘esto’ de esta manera es IMO no un olor a código, sino simplemente una preferencia personal. Por lo tanto, no es tan importante como la coherencia con el rest del código en el sistema. Si este código es inconsistente, puede cambiarlo para que coincida con el otro código. Si al cambiarlo introducirás inconsistencia con la mayoría del rest del código, eso es muy malo y lo dejaría en paz.

No querrás ponerte nunca en la posición de jugar al tenis de código donde alguien cambia algo puramente para que se vea “bien” solo para que alguien más venga después con diferentes gustos y luego lo cambie.

 class MyClass{ public: int x; void f(int xval); }; // void MyClass::f(int xval){ x = xval; } 

Siempre uso la convención de nomenclatura m_ Aunque me desagrada la “notación húngara” en general, me parece muy útil ver muy claramente si estoy trabajando con datos de miembros de la clase. Además, encontré el uso de 2 nombres de variables idénticas en el mismo ámbito y propenso a errores.

Estoy de acuerdo. No me gusta esa convención de nombres: prefiero una donde haya una distinción obvia entre las variables de los miembros y las variables locales. ¿Qué pasa si dejas de hacer this ?

En mi opinión, esto tiende a agregar desorden al código, por lo que tiendo a usar diferentes nombres de variables (dependiendo de la convención, podría ser un guión bajo, m_ , lo que sea).

 class MyClass { public: int m_x; void f(int p_x); }; void MyClass::f(int p_x) { m_x = p_x; } 

… es mi forma preferida de usar prefijos de scope. m_ para el miembro, p_ para el parámetro (algunos usan a_ para el argumento en su lugar), g_ para global y a veces l_ para local si ayuda a la legibilidad.

Si tiene dos variables que merecen el mismo nombre, esto puede ayudar mucho y evita tener que inventar alguna variación aleatoria en su significado solo para evitar la redefinición. O peor, los temidos ‘x2, x3, x4, etc.’ …

Es más normal en C ++ que los miembros se inicialicen en la construcción usando el inicializador.

Para hacer eso, debe usar un nombre diferente al nombre de la variable miembro.

Así que aunque usaría Foo(int x) { this.x = x; } Foo(int x) { this.x = x; } en Java, no lo haría en C ++.

El verdadero olor podría ser la falta de uso de iniciadores y métodos que no hacen otra cosa que mutar las variables miembro, en lugar del uso de this -> x sí mismo.

¿Alguien sabe por qué es una práctica universal en cada tienda de C ++ en la que he estado utilizar diferentes nombres para los argumentos de constructor a las variables miembro cuando se usan con los inicializadores? ¿Hubo algunos comstackdores de C ++ que no lo soportaban?

En la actualidad, la mayoría de los editores IDE colorean sus variables para indicar los miembros de la clase de las variables locales. Por lo tanto, IMO, ni prefijos o ‘this->’ deberían ser necesarios para la legibilidad.

No me gusta usar “esto” porque es atávico. Si estás progtwigndo en una buena C antigua (¿recuerdas C?), Y quieres imitar algunas de las características de OOP, creas una estructura con varios miembros (estos son análogos a las propiedades de tu objeto) y creas un conjunto de funciones que todas toman un puntero a esa estructura como su primer argumento (estas son análogas a los métodos de ese objeto).

(Creo que esta syntax typedef es correcta, pero ha pasado un tiempo …)

 typedef struct _myclass { int _x; } MyClass; void f(MyClass this, int x) { this->_x = x; } 

De hecho, creo que los comstackdores de C ++ más antiguos comstackrían tu código en el formulario anterior y luego lo pasarían al comstackdor de C. En otras palabras, C ++ hasta cierto punto era solo azúcar sintáctico. Así que no estoy seguro de por qué alguien querría progtwigr en C ++ y volver a usar explícitamente “this” en el código; tal vez sea “Nutrisweet sintáctico”.

Si tiene un problema con las convenciones de nomenclatura, puede intentar usar algo como lo siguiente.

 class tea { public: int cup; int spoon; tea(int cups, int spoons); }; 

o

 class tea { public: int cup; int spoon; tea(int drink, int sugar); }; 

Creo que entendiste la idea. Es básicamente nombrar las variables diferentes pero “iguales” en el sentido lógico. Espero que ayude.