C ++ a un amigo o no a un amigo

19

Tengo un programa orientado a objetos con un curso de C ++ este semestre en la universidad y aprendimos sobre las funciones de amigos.

Instintivamente, me disgustan por su capacidad de eludir la seguridad que brindan la Encapsulación y la ocultación de datos, leí algunos artículos en Internet y algunas personas pensaron que era una buena idea con algunos usos legítimos.

¿Qué diría un experto en OOP sobre las funciones de amigos en C ++? ¿Debería pasarlo por alto o debería aprender más al respecto?

nikhil
fuente
@todos: Respuestas y comentarios increíbles, esta es una excelente manera de aprender, no hay forma de que haya aprendido sobre amigos en esos detalles en un libro de texto.
nikhil

Respuestas:

13

No siempre es conveniente hacer que todas las funciones relacionadas con una clase C ++ sean miembros de esa clase. Por ejemplo, imagine una implementación de álgebra vectorial con multiplicación escalar. Queremos escribir:

 double a;
 Vector v, w;
 w = v * a;

Podemos hacer esto con una función miembro:

public class Vector {
 ...
 Vector operator*(double a);
}

Pero también nos gustaría escribir:

w = a * v

Esto requiere una función gratuita:

 Vector operator*(double a, Vector v)

La friendpalabra clave se agregó a C ++ para admitir este uso. La función libre es parte de la implementación de la clase Vector, y debe declararse en el mismo encabezado e implementarse en el mismo archivo fuente.

De manera similar, podemos usar friendpara simplificar la implementación de clases estrechamente acopladas, como una colección y un iterador. Nuevamente, declararía ambas clases en el mismo encabezado y las implementaría en el mismo archivo fuente.

Kevin Cline
fuente
3
"Esto requiere una función libre". No, no es así: inline Vector operator*(double a, Vector v) { return v*a; }. Solución canónica de hecho.
MSalters
1
@MSalters: Buen punto. Elegí un mal ejemplo. Creo que su función en línea es una función libre por definición, pero no se requiere una declaración de amigo.
Kevin Cline
44
@MSalters: eso es válido solo si * es conmutativo con respecto a a y v (x). Si los componentes del vector son genéricos (no necesariamente escalares), debe mantener el orden del operando
Emilio Garavaglia
Eso es bastante teórico. Quizás el único caso común no conmutativo sería inline Vector operator*(double a, Vector v) { return -v*a; }y que todavía no requiere amistad.
MSalters
16

Las funciones de amigo no son diferentes a las funciones de miembro en términos de encapsulación. Sin embargo, pueden ofrecer otras ventajas, como ser más genéricos, especialmente en lo que respecta a las plantillas. Además, algunos operadores solo pueden especificarse como funciones libres, por lo que si desea que tengan acceso de miembro, debe hacerlo friend.

Es mejor para frienduna sola función que verse obligado a hacer algo que no quiere que sea público. Eso significa que todo el mundo puede usarlo, en lugar de una sola función.

DeadMG
fuente
Las funciones de +1 para Friend no son diferentes a las funciones de miembro en términos de encapsulación. Sin embargo, esto solo es cierto para las funciones de miembros públicos.
TheFogger
1
@TheFogger: Podría decirse que también podría tener frienduna función que también sea "privada", como la declarada solo en una única TU.
DeadMG
5

Si te apasiona lo que haces, estarías aprendiendo todo sobre C ++. Aprenda para qué se usan, cómo usarlos y luego, y solo entonces, decida no usarlos. Como mínimo, estará preparado cuando lea el código de otra persona que usa esta faceta de C ++.

JK
fuente
5

" ¿Qué diría un experto en OOP? ... " Depende principalmente de cuán experto sea en C ++, eso, por su propia especificación, no es (y no quiere ser) un lenguaje para puristas.

OOP Zealots no usa C ++ (prefieren Smalltalk y les gusta Java).

Los zelots de programación funcional no usan C ++ (prefieren LISP y sus sucesores)

A la mayoría de los expertos en OOP no les gusta la función de amigo simplemente porque quieren que la parte OOP de C ++ se comporte como Smalltalk. Pero C ++ no es Smalltalk, y ni siquiera pueden entender que friend no rompa la encapsulación , por la sencilla razón de que una función no puede ser amiga de su clase sin que su clase lo desee .

Y desde el punto de vista de la "funcionalidad", entre a.fn(b)y fn(a,b)no hay diferencia (dónde fnhay un amigo): las partes involucradas son las mismas. Simplemente, una sintaxis puede ser más adecuada que otra: si fn es conmutativo con respecto a , ay probablemente sea más adecuado entonces (donde una apariencia tiene un "papel especial" que, de hecho, no lo es).bfn(a,b)a.fn(b)

Emilio Garavaglia
fuente
1
Los "fanáticos OOP" a quienes les gusta Java no han entendido OOP. Getters? Setters? ¿Sin sintaxis simple para cierres? Parafraseando a Alan Kay, no es así como se imaginó la POO.
Konrad Rudolph el
@Konrad: los fanáticos son un conjunto superiormente ilimitado. Siempre hay un fanático más fanático que un fanático dado.
Emilio Garavaglia
Tengo que decir que voté porque realmente me gustó el último párrafo. Tiene mucho sentido.
julealgon
5

¿"Amigo" viola la encapsulación?

No, no lo hace. "Amigo" es un mecanismo explícito para otorgar acceso, al igual que la membresía. No puede (en un programa conforme estándar) otorgarse acceso a una clase sin modificar su fuente.

flujo libre
fuente
2

Las preguntas frecuentes de C ++ son breves:

Use un miembro cuando pueda y un amigo cuando tenga que hacerlo.

Las preguntas frecuentes presentan una de las formas más útiles de pensar sobre la amistad:

Mucha gente piensa que la función de un amigo es algo fuera de la clase. En cambio, trate de pensar en una función de amigo como parte de la interfaz pública de la clase. Una función amiga en la declaración de clase no viola la encapsulación más de lo que una función miembro pública viola la encapsulación: ambas tienen exactamente la misma autoridad con respecto al acceso a las partes no públicas de la clase.

Quizás el uso más común de las funciones de amigo es sobrecargar << para E / S.

Gnawme
fuente
0

Las funciones de amigo se utilizan mejor para las definiciones de operador definidas por el usuario. Son útiles en otras situaciones, pero si te encuentras especificando clases de amigos con frecuencia, entonces puedes estar en un desvío de diseño (solo una buena autocomprobación para usar mientras escribes código).

Tenga cuidado con la declaración de "seguridad" en la pregunta original. Los modificadores de acceso están ahí para evitar que escribas código incorrecto por accidente, al igual que el compilador de alguna manera. Los modificadores de acceso limitan la interfaz y sirven para comunicar qué funciones son importantes para usar la clase (pública y protegida) y cuáles se crearon como parte de hacer que la clase sea más bonita para los mantenedores (privados). Los modificadores no constituyen seguridad, ya que hay muchas formas de obtener datos privados. Por ejemplo, obtenga un puntero a la clase y su tamaño, y vaya a pescar.

luego
fuente
-2

Las funciones de amigo de C ++ están estrechamente relacionadas con la siguiente funcionalidad:

  1. funciones libres
  2. funciones estáticas
  3. funciones de amigo

Esto significa que no tienen este puntero y, por lo tanto, están fuera de la clase / objeto. Por otro lado, a menudo toman parámetros que los hacen pertenecer nuevamente a la clase. Aquí hay un ejemplo que aclara el enlace:

class B;
class A {
public:
    friend void f(A &a, B &b);
private:
    int m_a;
};
class B {
public:
   friend void f(A &a, B &b);
private:
   int m_b;
};
void f(A &a, B &b) { /* uses both A's and B's private data */ }

La única diferencia entre las funciones estáticas y las funciones de amigo es que una función de amigo puede usar varias clases.

El uso del mecanismo amigo en c ++ requiere programadores que tengan aproximadamente 10-15 años de experiencia con la forma de programación c ++, por lo que inicialmente debe evitarlo. Es una función avanzada.

tp1
fuente
77
Y derivaste 10-15 años, ¿cómo?
DeadMG
De 10 a 15 años viene desde el momento en que se hizo realmente necesario.
tp1
3
Entonces, arbitrariamente inventaste un número, entonces.
DeadMG
3
-1: "Deberías evitarlo". Cada característica de C ++ fue creada para resolver un problema. Cuando surja ese problema, use la función adecuada.
Kevin Cline
Gracias por el -1. Había una razón para ese comentario. Supongo que es un concepto difícil que no todas las características son adecuadas para principiantes.
tp1