Una clase amiga en C++ puede acceder a los miembros privados y protegidos de la clase en la que se declara 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 la clase amiga permite ampliar 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 amiga, una función amiga es una función a la que se le da acceso a los miembros privados y protegidos de la clase en la que está declarada como amiga.
El siguiente ejemplo demuestra el uso de una clase amiga 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 <iostream> #include <memoria> #include <cadena> #include <conjunto_desordenado> clase Gráfico ; clase Vertex { público : explícito Vertex ( std :: string nombre ) : edges_ (), nombre_ ( std :: move ( nombre )) {} auto begin () const { return edges_.cbegin ( ); } auto end ( ) const { return edges_.cend ( ) ; } const auto & nombre () const { return nombre_ ; } privado : // Vertex otorga derechos de acceso a Graph. amigo clase Graph ; std :: conjunto_desordenado < Vértice *> aristas_ ; std :: cadena nombre_ ; }; clase Graph { public : ~ Graph () { while ( ! vertices_ . empty ()) { auto vertex = vertices_ . begin (); RemoveVertex ( * vertex ); } } auto AddVertex ( const std :: string & nombre ) - > Vertex * { auto vertex = std :: make_unique < Vertex > ( nombre ); auto iter = vertices_.insert ( vertex.get ( ) ) ; return vertex.release ( ) ; } void RemoveVertex ( Vertex * vertex ) { vertices_.erase ( vertex ) ; 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. from -> edges_ . insert ( to ); } auto begin ( ) const { retorna vertices_.cbegin ( ); } auto end ( ) const { retorna vertices_.cend ( ) ; } privado : std :: conjunto_desordenado < Vértice *> vértices_ ; };
Un uso adecuado de las clases amigas aumenta la encapsulación, ya que permite extender el acceso privado de una estructura de datos a sus partes (que son propiedad de la estructura de datos) 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 a sí misma acceso a la parte privada de otra clase; eso rompería la encapsulación. En lugar de eso, una clase da acceso a sus propias partes privadas a otra clase --- declarando esa clase como amiga. En el ejemplo del gráfico, Graph no puede declararse amiga de Vertex. En lugar de eso, Vertex declara a Graph como amiga y, por lo tanto, le proporciona a Graph 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 del gráfico, Vertex no puede acceder a los campos privados de Graph, aunque Graph sí puede acceder a los campos privados de Vertex.
Una característica similar, pero no equivalente, del lenguaje la proporciona la palabra clave modificadora de acceso de C# internal
, que permite que las clases dentro del mismo ensamblaje accedan a las partes privadas de otras clases. Esto corresponde a marcar cada clase como amiga de otra en el mismo ensamblaje; las clases amigas son más detalladas.
Los lenguajes de programación que no admiten clases amigas o una característica similar deberán implementar soluciones alternativas para lograr una interfaz segura basada en partes con una estructura de datos. Algunos ejemplos de estas soluciones alternativas son:
A
es amiga de la clase B
, la clase B
no es automáticamente amiga de la clase A
.A
es amiga de la clase B
, y la clase B
es amiga de la clase C
, la clase A
no es automáticamente amiga de la clase C
.Base
es amiga de la clase X
, la subclase Derived
no es automáticamente amiga de la clase X
; y si la clase X
es amiga de la clase Base
, la clase X
no es automáticamente amiga de la subclase Derived
. Sin embargo, si la clase Y
es amiga de la subclase Derived
, la clase Y
también tendrá acceso a las partes protegidas de la clase Base
, al igual que la subclase Derived
.