¿Es una mala idea tener un método de clase que se pasa variables de clase?

14

Esto es lo que quiero decir:

class MyClass {
    int arr1[100];
    int arr2[100];
    int len = 100;

    void add(int* x1, int* x2, int size) {
        for (int i = 0; i < size; i++) {
            x1[i] += x2[i];
        }
    }
};

int main() {
    MyClass myInstance;

    // Fill the arrays...

    myInstance.add(myInstance.arr1, myInstance.arr2, myInstance.len);
}

addya puede acceder a todas las variables que necesita, ya que es un método de clase, ¿es una mala idea? ¿Hay razones por las que debería o no debería hacer esto?

hombre de papel
fuente
13
Si tiene ganas de hacer esto, está insinuando un mal diseño. Las propiedades de la clase probablemente pertenecen a otro lugar.
bitsoflogic
11
El fragmento es demasiado pequeño. Este nivel de abstracciones mixtas no se produce en fragmentos de este tamaño. También puede solucionar esto con un buen texto sobre consideraciones de diseño.
Joshua
16
Sinceramente, no entiendo por qué incluso harías esto. Que ganas ¿Por qué no simplemente tener un addmétodo sin argumentos que opera directamente en sus componentes internos? ¿Solo porque?
Ian Kemp
2
@IanKemp O tiene un addmétodo que toma argumentos pero no existe como parte de una clase. Solo una función pura para agregar dos matrices juntas.
Kevin
¿El método add siempre agrega arr1 y arr2, o se puede usar para agregar algo a cualquier cosa?
user253751

Respuestas:

33

Hay muchas cosas con la clase que haría de manera diferente, pero para responder la pregunta directa, mi respuesta sería

si, es una mala idea

Mi razón principal para esto es que no tienes control sobre lo que se pasa a la addfunción. Seguro que espera que sea una de las matrices de miembros, pero ¿qué sucede si alguien pasa en una matriz diferente que tiene un tamaño menor a 100, o usted pasa en una longitud mayor a 100?

Lo que sucede es que ha creado la posibilidad de un desbordamiento del búfer. Y eso es algo malo por todas partes.

Para responder algunas preguntas más obvias (para mí):

  1. Está mezclando matrices de estilo C con C ++. No soy un gurú de C ++, pero sí sé que C ++ tiene formas mejores (más seguras) de manejar matrices

  2. Si la clase ya tiene las variables miembro, ¿por qué necesita pasarlas? Esto es más una cuestión arquitectónica.

Otras personas con más experiencia en C ++ (dejé de usarlo hace 10 o 15 años) pueden tener formas más elocuentes de explicar los problemas, y probablemente también tengan más problemas.

Peter M
fuente
De hecho, vector<int>sería más adecuado; la longitud sería inútil. Alternativamente const int len, dado que la matriz de longitud variable no es parte del estándar C ++ (aunque algunos compiladores lo admiten, porque es una característica opcional en C).
Christophe
55
@Christophe Author estaba usando una matriz de tamaño fijo, que debe reemplazarse por una std::arrayque no se descompone al pasarla a los parámetros, tiene una forma más conveniente de obtener su tamaño, es copiable y tiene acceso con verificación de límites opcional, sin tener sobrecarga Matrices de estilo C.
Cubic
1
@Cubic: cada vez que veo código que declara matrices con un tamaño fijo arbitrario (como 100), estoy bastante seguro de que el autor es un principiante que no tiene idea de las implicaciones de estas tonterías, y es una buena idea recomendarlo. cambiando este diseño a algo con un tamaño variable.
Doc Brown
Los arreglos están bien.
Lightness compite con Monica el
... para aquellos que no entendieron lo que quiero decir, ver también Zero One Infinity Rule
Doc Brown
48

Llamar a un método de clase con algunas variables de clase no es necesariamente malo. Pero hacerlo desde fuera de la clase es una muy mala idea y sugiere una falla fundamental en su diseño OO, a saber, la ausencia de una encapsulación adecuada :

  • Cualquier código que use su clase necesitaría saber que lenes la longitud de la matriz, y usarlo consistentemente. Esto va en contra del principio del mínimo conocimiento . Dicha dependencia de los detalles internos de la clase es extremadamente propensa a errores y arriesgada.
  • Esto haría que la evolución de la clase sea muy difícil (por ejemplo, si algún día desea cambiar las matrices heredadas lena una más moderna std::vector<int>), ya que requeriría que también cambie todo el código usando su clase.
  • Cualquier parte del código podría causar estragos en sus MyClassobjetos al corromper algunas variables públicas sin respetar las reglas (llamadas invariantes de clase )
  • Finalmente, el método es en realidad independiente de la clase, ya que funciona solo con los parámetros y no depende de ningún otro elemento de clase. Este tipo de método podría ser una función independiente fuera de la clase. O al menos un método estático.

Debe refactorizar su código y:

  • haga sus variables de clase privateo protected, a menos que haya una buena razón para no hacerlo.
  • diseñe sus métodos públicos como acciones a realizar en la clase (por ejemplo:) object.action(additional parameters for the action).
  • Si después de esta refactorización, todavía tiene algunos métodos de clase que deben llamarse con variables de clase, hágalos protegidos o privados después de haber verificado que son funciones de utilidad que admiten los métodos públicos.
Christophe
fuente
7

Cuando la intención de este diseño es que desea poder reutilizar este método para datos que no provienen de esta instancia de clase, es posible que desee declararlo como un staticmétodo .

Un método estático no pertenece a ninguna instancia particular de una clase. Es más bien un método de la clase misma. Debido a que los métodos estáticos no se ejecutan en el contexto de ninguna instancia particular de la clase, solo pueden acceder a las variables miembro que también se declaran como estáticas. Teniendo en cuenta que su método addno hace referencia a ninguna de las variables miembro declaradas en MyClass, es un buen candidato para ser declarado como estático.

Sin embargo, los problemas de seguridad mencionados por las otras respuestas son válidos: no está comprobando si ambas matrices son al menos lenlargas. Si desea escribir C ++ moderno y robusto, debe evitar el uso de matrices. Utilice la clase en su std::vectorlugar siempre que sea posible. Contrariamente a las matrices regulares, los vectores conocen su propio tamaño. Por lo tanto, no necesita hacer un seguimiento de su tamaño. La mayoría (¡no todos!) De sus métodos también realizan comprobaciones de límite automáticas, lo que garantiza que obtendrá una excepción cuando lea o escriba más allá del final. El acceso regular a la matriz no realiza la comprobación de límites, lo que resulta en un defecto de seguridad en el mejor de los casos y puede causar una vulnerabilidad de desbordamiento de búfer explotable en el peor de los casos .

Philipp
fuente
1

Un método en una clase idealmente manipula datos encapsulados dentro de la clase. En su ejemplo, no hay ninguna razón para que el método add tenga parámetros, simplemente use las propiedades de la clase.

Rik D
fuente
0

Pasar (parte de) un objeto a una función miembro está bien. Al implementar sus funciones, siempre debe ser consciente de posibles alias y sus consecuencias.

Por supuesto, el contrato puede dejar algunos tipos de alias indefinidos incluso si el idioma lo admite.

Ejemplos destacados:

  • Autoasignación Esto debería ser, posiblemente costoso, sin operaciones. No peses el caso común para ello.
    La asignación de auto-movimiento, por otro lado, aunque no debería explotar en tu cara, no tiene por qué ser un no-op.
  • Insertar una copia de un elemento en un contenedor en el contenedor. Algo así como v.push_back(v[2]);.
  • std::copy() asume que el destino no comienza dentro de la fuente.

Pero tu ejemplo tiene diferentes problemas:

  • La función miembro no utiliza ninguno de sus privilegios, ni se refiere a this. Actúa como una función de utilidad gratuita que simplemente está en el lugar equivocado.
  • Tu clase no intenta presentar ningún tipo de abstracción . En línea con eso, no intenta encapsular sus entrañas ni mantener invariantes . Es una bolsa tonta de elementos arbitrarios.

En resumen: sí, este ejemplo es malo. Pero no porque la función miembro pase objetos miembro.

Deduplicador
fuente
0

Hacer esto específicamente es una mala idea por varias buenas razones, pero no estoy seguro de que todas sean parte integral de su pregunta:

  • Tener variables de clase (variables reales, no constantes) es algo que debe evitarse si es posible, y debería provocar una reflexión seria sobre si es absolutamente necesario.
  • Dejar el acceso de escritura a estas variables de clase completamente abierto para todos es casi universalmente malo.
  • Su método de clase termina siendo ajeno a su clase. Lo que realmente es es una función que agrega los valores de una matriz a los valores de otra matriz. Este tipo de función debería ser una función en un lenguaje que tiene funciones, y debería ser un método estático de una "clase falsa" (es decir, una colección de funciones sin datos ni comportamiento de objeto) en lenguajes que no tienen funciones.
  • Alternativamente, elimine estos parámetros y conviértalo en un método de clase real, pero aún sería malo por las razones mencionadas anteriormente.
  • ¿Por qué int arrays? vector<int>Haría tu vida más fácil.
  • Si este ejemplo es realmente representativo de su diseño, considere colocar sus datos en objetos y no en clases y también implementar constructores para estos objetos de una manera que no requiera cambiar el valor de los datos del objeto después de la creación.
Kafein
fuente
0

Este es un enfoque clásico de "C con clases" para C ++. En realidad, esto no es lo que escribiría cualquier programador experimentado de C ++. Por un lado, el uso de matrices C sin procesar es bastante universalmente desaconsejado, a menos que esté implementando una biblioteca de contenedores.

Algo como esto sería más apropiado:

// Don't forget to compile with -std=c++17
#include <iostream>
using std::cout; // This style is less common, but I prefer it
using std::endl; // I'm sure it'll incite some strongly opinionated comments

#include <array>
using std::array;

#include <algorithm>

#include <vector>
using std::vector;


class MyClass {
public: // Begin lazy for brevity; don't do this
    std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
    std::array<int, 5> arr2 = {10, 10, 10, 10, 10};
};

void elementwise_add_assign(
    std::array<int, 5>& lhs,
    const std::array<int, 5>& rhs
) {
    std::transform(
        lhs.begin(), lhs.end(), rhs.begin(),
        lhs.begin(),
        [](auto& l, const auto& r) -> int {
            return l + r;
        });
}

int main() {
    MyClass foo{};

    elementwise_add_assign(foo.arr1, foo.arr2);

    for(auto const& value: foo.arr1) {
        cout << value << endl; // arr1 values have been added to
    }

    for(auto const& value: foo.arr2) {
        cout << value << endl; // arr2 values remain unaltered
    }
}
Alexander - Restablece a Monica
fuente