Convenciones para métodos de acceso (getters y setters) en C++


Se han hecho varias preguntas sobre los métodos de acceso en C++, pero ninguna fue capaz de satisfacer mi curiosidad sobre el tema.

Trato de evitar los accesores siempre que sea posible, porque, como Stroustrup y otros programadores famosos, considero una clase con muchos de ellos un signo de mal OO. En C++, en la mayoría de los casos puedo agregar más responsabilidad a una clase o usar la palabra clave amigo para evitarlas. Sin embargo, en algunos casos, realmente necesita acceso a miembros específicos de la clase.

Hay varias posibilidades:

1. No use accesores en absoluto

Solo podemos hacer públicas las variables de miembro respectivas. Esto es un no-go en Java, pero parece estar bien con la comunidad de C++. Sin embargo, estoy un poco preocupado por los casos en los que se debe devolver una copia explícita o una referencia de solo lectura (const) a un objeto, ¿es exagerado?

2. Use métodos get/set al estilo Java

No estoy seguro de si es de Java en absoluto, pero quiero decir esto:

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3. Use los métodos get/set de objective C-style

Esto es un poco raro, pero aparentemente cada vez más común:

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

Para que eso funcione, tendrá que encontrar un nombre diferente para su variable miembro. Algunas personas añaden un subrayado, otras anteponen "m_". A mí tampoco me gusta.

¿Qué estilo usas y por qué?

Author: Noarth, 2010-09-05

9 answers

Desde mi perspectiva como sentado con 4 millones de líneas de código C++ (y eso es solo un proyecto) desde una perspectiva de mantenimiento yo diría:

  • Está bien no usar getters / setters si los miembros son inmutables (es decir, const) o simples sin dependencias (como una clase de puntos con miembros X e Y).

  • Si el miembro es private solamente, también está bien omitir getters/setters. También cuento los miembros de interno pimpl-clases como private si el .unidad cpp es pequeñito.

  • Si el miembro es public o protected (protected es tan malo como public) y no-const, no simples o tiene dependencias, a continuación, utilice los métodos getter/setter.

Como chico de mantenimiento mi principal razón para querer tener getters/setters es porque entonces tengo un lugar para poner puntos de interrupción / registro / algo más.

Prefiero el estilo de la alternativa 2. como eso es más buscable (un componente clave en la escritura de código mantenible).

 36
Author: FuleSnabel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-24 12:41:01
  1. Nunca uso este estilo. Porque puede limitar el futuro del diseño de tu clase y los geters o setters explícitos son igual de eficientes con un buen compilador.

    Por supuesto, en realidad los getters o setters explícitos en línea crean la misma dependencia subyacente en la implementación de la clase. Solo reducen la dependencia semántica. Todavía tienes que recompilar todo si los cambias.

  2. Este es mi estilo predeterminado cuando uso accessor método.

  3. Este estilo me parece demasiado "inteligente". Lo uso en raras ocasiones, pero solo en casos en los que realmente quiero que el accessor se sienta tanto como sea posible como una variable.

Creo que hay un caso para bolsas simples de variables con posiblemente un constructor para asegurarse de que todos están inicializados a algo cuerdo. Cuando hago esto, simplemente lo hago un struct y lo dejo todo público.

 7
Author: Omnifarious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-05 19:45:42

2) es la mejor OMI, porque hace que sus intenciones más claras. set_amount(10) es más significativo que amount(10), y como un buen efecto secundario permite a un miembro llamado amount.

Variables públicas es generalmente una mala idea, porque no hay encapsulación. Supongamos que necesita actualizar una caché o actualizar una ventana cuando se actualiza una variable? Lástima si tus variables son públicas. Si tiene un método establecido, puede agregarlo allí.

 7
Author: AshleysBrain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-05 19:58:36
  1. Ese es un buen estilo si solo queremos representar pure datos.

  2. No me gusta :) porque get_/set_ es realmente innecesario cuando podemos sobrecargarlos en C++.

  3. STL utiliza este estilo, como std::streamString::str y std::ios_base::flags, excepto cuando se debe evitar! ¿Cuando? Cuando el nombre del método entra en conflicto con el nombre de otro tipo, se usa el estilo get_/set_, como std::string::get_allocator debido a std::allocator.

 6
Author: M. Sadeq H. E.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-06 01:49:56

En general, siento que no es una buena idea tener demasiados getters y setters siendo utilizados por demasiadas entidades en el sistema. Es solo una indicación de un mal diseño o encapsulación incorrecta.

Dicho esto, si tal diseño necesita ser refactorizado, y el código fuente está disponible, preferiría usar el patrón de Diseño del visitante. La razón es:

A. Le da a una clase la oportunidad de decidir a quién permitir el acceso a su estado privado

B. Da una clase an oportunidad de decidir qué acceso a permitir a cada una de las entidades que son interesado en su estado privado

C. It documenta claramente tal acceso externo a través de una interfaz de clase clara

La idea básica es:

A) Rediseñar si es posible,

B) Refactor tal que

  1. Todo acceso al estado de clase es a través de un bien conocido individualista interfaz

  2. It debería ser posible configurar algún tipo de hacer y no hacer a cada una de estas interfaces, por ejemplo, todas acceso desde entidad externa BUENO debe ser permitido, todo el acceso desde entidad externa MAL debe ser no permitido, y entidad externa OK se debe permitir obtener pero no establecer (por ejemplo)

 4
Author: Chubsdad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-06 02:32:29
  1. No excluiría el uso de los accesores. Puede que para algunas estructuras POD, pero los considero una buena cosa (algunos accesores podrían tener lógica adicional, también).

  2. Realmente no importa la convención de nomenclatura, si usted es consistente en su código. Si está utilizando varias bibliotecas de terceros, es posible que usen diferentes convenciones de nomenclatura de todos modos. Así que es una cuestión de gustos.

 2
Author: Cătălin Pitiș,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-05 19:34:36

He visto la idealización de clases en lugar de tipos integrales para referirse a datos significativos.

Algo como esto a continuación generalmente no está haciendo un buen uso de las propiedades de C++:

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

¿Por qué? Porque el resultado de p. masa*p. aceleración es un flotador y no una fuerza como se esperaba.

La definición de clases para designar un propósito (incluso si es un valor, como amount mencionado anteriormente) tiene más sentido, y nos permite hacer algo como:

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

Usted puede acceder al valor en cantidad implícitamente por el operador int. Además:

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Uso de Getter/Setter:

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

Este es un enfoque diferente, no significa que sea bueno o malo, sino diferente.

 1
Author: gus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-20 21:36:47

Permítanme hablarles de una posibilidad adicional, que parece la más conscrita.

Necesita leer y modificar

Simplemente declare esa variable pública:

class Worker {
public:
    int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

Basta con leer

class Worker {
    int _wage = 5000;
public:
    inline int wage() {
        return _wage;
    }
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

La desventaja de este enfoque es que necesita cambiar todo el código de llamada (agregar paréntesis, es decir) cuando desea cambiar el patrón de acceso.

 0
Author: Rok Kralj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-15 12:51:52

Una posibilidad adicional podría ser :

int& amount();

No estoy seguro de recomendarlo, pero tiene la ventaja de que la notación inusual puede evitar que los usuarios modifiquen los datos.

str.length() = 5; // Ok string is a very bad example :)

A veces es tal vez solo la buena elección a hacer:

image(point) = 255;  

Otra posibilidad de nuevo, utilice la notación funcional para modificar el objeto.

edit::change_amount(obj, val)

De esta manera la función dangerous/editing se puede extraer en un espacio de nombres separado con su propia documentación. Este parece venga naturalmente con la programación genérica.

 0
Author: log0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 14:06:39