Una clase amiga en C ++ puede acceder a los miembros privados y protegidos de la clase en la que está declarada como amiga. [1] Un uso significativo de una clase amiga es que una parte de una estructura de datos, representada por una clase, proporcione acceso a la clase principal que representa esa estructura de datos. El mecanismo de clase amigo permite extender el almacenamiento y el acceso a las partes, al tiempo que conserva la encapsulación adecuada tal como la ven los usuarios de la estructura de datos.
Similar a una clase de amigo, una función de amigo es una función a la que se le da acceso a los miembros privados y protegidos de la clase en la que se declara como amigo.
Ejemplo
El siguiente ejemplo demuestra el uso de una clase de amigo para una estructura de datos de gráfico, donde el gráfico está representado por la clase principal Graph y los vértices del gráfico están representados por la clase Vertex.
#include #include #include #include clase Graph ;clase Vertex { público : explícita Vertex ( std :: string nombre ) : edges_ (), name_ ( std :: movimiento ( nombre )) {} auto begin () const { return bordes_ . cbegin (); } auto end () const { return bordes_ . cend (); } const auto & name () const { return name_ ; } privado : // Vertex otorga derechos de acceso a Graph. Gráfico de clase de amigo ; std :: unordered_set < Vértice *> bordes_ ; std :: string name_ ; };class Graph { public : ~ Graph () { while ( ! vertices_ . empty ()) { auto vertex = vertices_ . comenzar (); RemoveVertex ( * vértice ); } } auto AddVertex ( const std :: string & name ) -> Vertex * { auto vertex = std :: make_unique < Vertex > ( name ); auto iter = vértices_ . insertar ( vértice . obtener ()); volver vértice . liberación (); } void RemoveVertex ( Vértice * vértice ) { vértices_ . borrar ( vértice ); eliminar vértice ; } auto AddEdge ( Vertex * from , Vertex * to ) { // Graph puede acceder a los campos privados de Vertex porque Vertex declaró a Graph como // un amigo. desde -> bordes_ . insertar ( a ); } auto begin () const { return vertices_ . cbegin (); } auto end () const { return vertices_ . cend (); } privado : std :: unordered_set < Vértice *> vértices_ ; };
Encapsulamiento
Un uso adecuado de las clases de amigos aumenta la encapsulación, porque permite extender el acceso privado de una estructura de datos a sus partes --- que la estructura de datos posee --- sin permitir el acceso privado a ninguna otra clase externa. De esta manera, la estructura de datos permanece protegida contra intentos accidentales de romper las invariantes de la estructura de datos desde el exterior.
Es importante notar que una clase no puede darse acceso a la parte privada de otra clase; eso rompería la encapsulación. Más bien, una clase da acceso a sus propias partes privadas a otra clase --- declarando esa clase como amiga. En el ejemplo de gráfico, Graph no puede declararse un Vertex amigo. Más bien, Vertex declara que Graph es un amigo y, por lo tanto, proporciona a Graph un acceso a sus campos privados.
El hecho de que una clase elija a sus propios amigos significa que la amistad no es simétrica en general. En el ejemplo de gráfico, Vertex no puede acceder a los campos privados de Graph, aunque Graph puede acceder a los campos privados de Vertex.
Alternativas
La palabra clave interna de C # proporciona una característica de lenguaje similar, pero no equivalente, que permite que las clases dentro del mismo ensamblado accedan a las partes privadas de otras clases. Esto corresponde a marcar cada clase como amiga de otra en la misma asamblea; las clases de amigos son más detalladas.
Los lenguajes de programación que carecen de soporte para clases de amigos, o una característica de lenguaje similar, tendrán que implementar soluciones para lograr una interfaz segura basada en partes para una estructura de datos. Ejemplos de tales soluciones son:
- Haga públicos los campos de las piezas. Esta solución disminuye la encapsulación al permitir violar las invariantes de la estructura de datos desde el exterior.
- Mueva todos los datos estructurales mutables de la parte a la estructura de datos e introduzca la indirección de nuevo desde cada parte a su estructura de datos. Esta solución cambia la organización de la estructura de datos y aumenta el consumo de memoria en los casos en los que, de otro modo, no habría necesidad de esta información.
Propiedades
- Las amistades no son simétricas : si la clase
A
es amiga de la claseB
, la claseB
no es automáticamente amiga de la claseA
. - Las amistades no son transitivas : si la clase
A
es un amigo de la claseB
y la claseB
es un amigo de la claseC
, la claseA
no es automáticamente un amigo de la claseC
. - Las amistades no se heredan : si la clase
Base
es amiga de la claseX
, la subclaseDerived
no es automáticamente amiga de la claseX
; y si la claseX
es amiga de la claseBase
, la claseX
no es automáticamente amiga de la subclaseDerived
. Sin embargo, si la claseY
es amiga de la subclaseDerived
, la claseY
también tendrá acceso a las partes protegidas de la claseBase
, tal como loDerived
hace la subclase .