Pregunta

He estado leyendo el Preguntas frecuentes sobre C++ y tenía curiosidad por el friend declaración.Personalmente nunca lo he usado, sin embargo estoy interesado en explorar el idioma.

¿Cuál es un buen ejemplo de uso? friend?


Al leer las preguntas frecuentes un poco más, me gusta la idea del << >> operador sobrecargando y agregando como amigo de esas clases.Sin embargo, no estoy seguro de cómo esto no rompe la encapsulación.¿Cuándo pueden estas excepciones mantenerse dentro del rigor de la programación orientada a objetos?

¿Fue útil?

Solución

En primer lugar (en mi opinión), no escuches a las personas que dicen friend no es útil.Es útil.En muchas situaciones, tendrá objetos con datos o funcionalidades que no están destinados a estar disponibles públicamente.Esto es particularmente cierto en el caso de grandes bases de código con muchos autores que pueden estar sólo superficialmente familiarizados con diferentes áreas.

HAY alternativas al especificador de amigos, pero a menudo son engorrosas (clases concretas de nivel cpp/definiciones de tipos enmascaradas) o no infalibles (comentarios o convenciones de nombres de funciones).

Sobre la respuesta;

El friend El especificador permite que la clase designada acceda a datos o funciones protegidos dentro de la clase que realiza la declaración de amigo.Por ejemplo, en el siguiente código, cualquiera puede preguntarle a un niño su nombre, pero solo la madre y el niño pueden cambiar el nombre.

Puede llevar este ejemplo simple más allá considerando una clase más compleja como una ventana.Es muy probable que una ventana tenga muchos elementos de función/datos que no deberían ser accesibles públicamente, pero que SON necesarios para una clase relacionada, como un WindowManager.

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};

Otros consejos

en el trabajo nosotros usar amigos para probar el código, extensamente.Significa que podemos proporcionar una encapsulación y ocultación de información adecuadas para el código de la aplicación principal.Pero también podemos tener un código de prueba separado que utilice amigos para inspeccionar el estado interno y los datos para realizar pruebas.

Basta decir que no usaría la palabra clave amigo como componente esencial de su diseño.

El friend La palabra clave tiene varios buenos usos.Aquí están los dos usos inmediatamente visibles para mí:

Definición de amigo

La definición de amigo permite definir una función en el ámbito de la clase, pero la función no se definirá como una función miembro, sino como una función libre del espacio de nombres adjunto, y no será visible normalmente excepto para la búsqueda dependiente de argumentos.Eso lo hace especialmente útil para la sobrecarga de operadores:

namespace utils {
    class f {
    private:
        typedef int int_type;
        int_type value;

    public:
        // let's assume it doesn't only need .value, but some
        // internal stuff.
        friend f operator+(f const& a, f const& b) {
            // name resolution finds names in class-scope. 
            // int_type is visible here.
            return f(a.value + b.value);
        }

        int getValue() const { return value; }
    };
}

int main() {
    utils::f a, b;
    std::cout << (a + b).getValue(); // valid
}

Clase base CRTP privada

A veces, se encuentra la necesidad de que una política necesite acceso a la clase derivada:

// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
    void doSomething() {
        // casting this to Derived* requires us to see that we are a 
        // base-class of Derived.
        some_type const& t = static_cast<Derived*>(this)->getSomething();
    }
};

// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
    // we derive privately, so the base-class wouldn't notice that, 
    // (even though it's the base itself!), so we need a friend declaration
    // to make the base a friend of us.
    friend class SomePolicy<FlexibleClass>;

    void doStuff() {
         // calls doSomething of the policy
         this->doSomething();
    }

    // will return useful information
    some_type getSomething();
};

Encontrará un ejemplo no artificial para esto en este respuesta.Otro código que usa eso está en este respuesta.La base CRTP lanza su puntero this para poder acceder a los campos de datos de la clase derivada utilizando punteros de miembros de datos.

@roo:La encapsulación no se rompe aquí porque la propia clase dicta quién puede acceder a sus miembros privados.La encapsulación solo se rompería si esto pudiera ser causado desde fuera de la clase, por ejemplo.si tu operator << proclamaría “Soy un amigo de clase foo.”

friend reemplaza el uso de public, no uso de private!

En realidad, las preguntas frecuentes sobre C++ ya responde esto.

El ejemplo canónico es sobrecargar el operador<<.Otro uso común es permitir que una clase de ayudante o administrador acceda a sus componentes internos.

Aquí hay un par de pautas que escuché sobre los amigos de C++.El último es particularmente memorable.

  • Tus amigos no son los amigos de tu hijo.
  • Los amigos de su hijo no son sus amigos.
  • Sólo tus amigos pueden tocar tus partes privadas.

editar:Al leer las preguntas frecuentes un poco más, me gusta la idea de que el operador << >> se sobrecargue y se agregue como amigo de esas clases, sin embargo, no estoy seguro de cómo esto no rompe la encapsulación.

¿Cómo rompería la encapsulación?

Rompes la encapsulación cuando lo permites irrestricto acceso a un miembro de datos.Considere las siguientes clases:

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

c1 es obviamente no encapsulado.Cualquiera puede leer y modificar x en eso.No tenemos forma de hacer cumplir ningún tipo de control de acceso.

c2 obviamente está encapsulado.No hay acceso público a x.Lo único que puedes hacer es llamar al foo función, que realiza alguna operación significativa en la clase.

c3?¿Está eso menos encapsulado?¿Permite el acceso sin restricciones a x?¿Permite el acceso a funciones desconocidas?

No.Permite precisamente uno función para acceder a los miembros privados de la clase.Al igual que c2 hizo.Y al igual que c2, la única función a la que tiene acceso no es "alguna función aleatoria y desconocida", sino "la función enumerada en la definición de clase".Al igual que c2, podemos ver, con solo mirar las definiciones de clases, una completo lista de quién tiene acceso.

Entonces, ¿exactamente cómo es que esto está menos encapsulado?La misma cantidad de código tiene acceso a los miembros privados de la clase.Y todos quién tiene acceso aparece en la definición de clase.

friend no rompe la encapsulación.Esto hace que algunos programadores de Java se sientan incómodos, porque cuando dicen "OOP", en realidad significar "Java".Cuando dicen "Encapsulación", no quieren decir "los miembros privados deben estar protegidos de accesos arbitrarios", sino "una clase Java donde las únicas funciones capaces de acceder a miembros privados son los miembros de la clase", aunque esto es una completa tontería. por varias razones.

En primer lugar, como ya se ha demostrado, es demasiado restrictivo.No hay ninguna razón por la que no se deba permitir que los métodos amigos hagan lo mismo.

En segundo lugar, no es restrictivo. suficiente.Considere una cuarta clase:

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

Esto, según la mentalidad Java antes mencionada, está perfectamente encapsulado.Y, sin embargo, permite que absolutamente cualquiera pueda leer y modificar x.¿Cómo es que aún tiene sentido?(pista:No es así)

Línea de fondo:La encapsulación consiste en poder controlar qué funciones pueden acceder a miembros privados.Es no sobre dónde se encuentran precisamente las definiciones de estas funciones.

Otra versión común del ejemplo de Andrew, el temido pareado de códigos

parent.addChild(child);
child.setParent(parent);

En lugar de preocuparse si ambas líneas siempre se hacen juntas y en orden consistente, puede hacer que los métodos sean privados y tener una función amiga para reforzar la coherencia:

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

En otras palabras, puede mantener las interfaces públicas más pequeñas y aplicar invariantes que abarquen clases y objetos en funciones amigas.

Usted controla los derechos de acceso de los miembros y funciones utilizando Privado/Protegido/Público, ¿verdad?entonces suponiendo que la idea de todos y cada uno de esos 3 niveles esté clara, entonces debería quedar claro que nos estamos perdiendo algo...

La declaración de un miembro/función como protegida, por ejemplo, es bastante genérica.Estás diciendo que esta función está fuera del alcance de todos (a excepción de un hijo heredado, por supuesto).Pero ¿qué pasa con las excepciones?Cada sistema de seguridad te permite tener algún tipo de "lista blanca", ¿verdad?

Entonces amigo le permite tener la flexibilidad de tener un aislamiento de objetos sólido como una roca, pero permite crear una "laguna" para las cosas que considere justificadas.

Supongo que la gente dice que no es necesario porque siempre hay un diseño que funcionará sin él.Creo que es similar a la discusión de variables globales:Nunca debes usarlos, siempre hay una manera de prescindir de ellos...pero en realidad, se ven casos en los que esa termina siendo la forma (casi) más elegante...Creo que este es el mismo caso con los amigos.

Realmente no sirve de nada, aparte de permitirle acceder a una variable miembro sin usar una función de configuración.

Bueno, esa no es exactamente la forma de verlo.La idea es controlar quién puede acceder a qué, teniendo o no un función de configuración poco tiene que ver.

Encontré un lugar útil para usar el acceso de amigos:Prueba unitaria de funciones privadas.

Friend resulta útil cuando estás creando un contenedor y quieres implementar un iterador para esa clase.

La respuesta corta sería:usar amigo cuando en realidad mejora encapsulación.Mejorar la legibilidad y la usabilidad (los operadores << y >> son el ejemplo canónico) también es una buena razón.

En cuanto a ejemplos de mejora de la encapsulación, las clases diseñadas específicamente para trabajar con las partes internas de otras clases (me vienen a la mente las clases de prueba) son buenas candidatas.

El creador de C++ dice que eso no viola ningún principio de encapsulación, y lo citaré:

¿"amigo" viola la encapsulación?No.No es asi."Amigo" es un mecanismo explícito para otorgar acceso, al igual que la membresía.No puede (en un programa conforme a estándar) otorgarse acceso a una clase sin modificar su fuente.

Está más que claro...

Surgió un problema interesante en una empresa en la que trabajé anteriormente y donde usábamos amigo para lograr un afecto decente.Trabajé en nuestro departamento de marco y creamos un sistema de nivel de motor básico sobre nuestro sistema operativo personalizado.Internamente teníamos una estructura de clases:

         Game
        /    \
 TwoPlayer  SinglePlayer

Todas estas clases fueron parte del marco y mantenidas por nuestro equipo.Los juegos producidos por la empresa se construyeron sobre este marco derivado de uno de los Juegos infantiles.El problema era que Game tenía interfaces para varias cosas a las que SinglePlayer y TwoPlayer necesitaban acceso pero que no queríamos exponer fuera de las clases del marco.La solución fue hacer que esas interfaces fueran privadas y permitir que TwoPlayer y SinglePlayer accedieran a ellas a través de la amistad.

La verdad es que todo este problema podría haberse resuelto con una mejor implementación de nuestro sistema, pero estábamos atrapados en lo que teníamos.

Otro uso: amigo (+ herencia virtual) se puede utilizar para evitar derivar de una clase (también conocido como:"hacer que una clase no sea derivable") => 1, 2

De 2:

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 

Para hacer TDD muchas veces he usado la palabra clave "amigo" en C++.

¿Puede un amigo saber todo sobre mí?


Actualizado:Encontré esta valiosa respuesta sobre la palabra clave "amigo" de Sitio de Bjarne Stroustrup.

"Amigo" es un mecanismo explícito para otorgar acceso, al igual que la membresía.

Con respecto al operador<< y operador>> no hay una buena razón para hacer amigos a estos operadores.Es cierto que no deberían ser funciones de miembros, pero tampoco es necesario que sean amigos.

Lo mejor que puedes hacer es crear funciones públicas de impresión (ostream&) y lectura (istream&).Luego, escribe el operador<< y el operador>> en términos de esas funciones.Esto brinda el beneficio adicional de permitirle hacer que esas funciones sean virtuales, lo que proporciona serialización virtual.

Solo estoy usando la palabra clave amigo para realizar pruebas unitarias de funciones protegidas.Algunos dirán que no deberías probar la funcionalidad protegida.Sin embargo, encuentro esta herramienta muy útil cuando agrego nuevas funciones.

Sin embargo, no uso la palabra clave in directamente en las declaraciones de clase, sino que uso un ingenioso truco de plantilla para lograr esto:

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

Esto me permite hacer lo siguiente:

friendMe(this, someClassInstance).someProtectedFunction();

Funciona al menos en GCC y MSVC.

Tienes que tener mucho cuidado sobre cuándo y dónde utilizas el friend palabra clave y, como usted, la he usado muy raramente.A continuación se presentan algunas notas sobre el uso friend y las alternativas.

Digamos que quieres comparar dos objetos para ver si son iguales.Podrías:

  • Utilice métodos de acceso para hacer la comparación (verifique cada ivar y determine la igualdad).
  • O bien, puede acceder a todos los miembros directamente haciéndolos públicos.

El problema con la primera opción es que podrían ser MUCHOS descriptores de acceso, lo cual es (ligeramente) más lento que el acceso directo a variables, más difícil de leer y engorroso.El problema con el segundo enfoque es que se rompe por completo la encapsulación.

Lo que sería bueno es si pudiéramos definir una función externa que aún pudiera tener acceso a los miembros privados de una clase.Podemos hacer esto con el friend palabra clave:

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

El método equal(Beer, Beer) ahora tiene acceso directo a a y bmiembros privados (que pueden ser char *brand, float percentAlcohol, etc.Este es un ejemplo bastante artificial; antes aplicaría friend a una sobrecargada == operator, pero llegaremos a eso.

Algunas cosas a tener en cuenta:

  • A friend NO es una función miembro de la clase
  • Es una función ordinaria con acceso especial a los miembros privados de la clase.
  • No reemplace todos los accesores y mutadores con amigos (también puede hacer que todo public!)
  • La amistad no es recíproca.
  • La amistad no es transitiva.
  • La amistad no se hereda.
  • O, como el Las preguntas frecuentes sobre C++ explican:"El hecho de que te conceda acceso a mí por amistad no otorga automáticamente a tus hijos acceso a mí, no otorga automáticamente a tus amigos acceso a mí y no me otorga automáticamente acceso a ti".

Realmente solo uso friends cuando es mucho más difícil hacerlo al revés.Como otro ejemplo, muchas funciones matemáticas vectoriales a menudo se crean como friends debido a la interoperabilidad de Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4, etc.Y es mucho más fácil ser amigos que tener que utilizar descriptores de acceso en todas partes.Como se ha señalado, friend suele ser útil cuando se aplica a la << (muy útil para depurar), >> y tal vez el == operador, pero también se puede utilizar para algo como esto:

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

Como digo, no uso friend muy a menudo, pero de vez en cuando es justo lo que necesitas.¡Espero que esto ayude!

El ejemplo del árbol es un ejemplo bastante bueno:Tener un objeto implementado en algunas clases diferentes sin tener una relación de herencia.

Tal vez también podría necesitarlo para tener un constructor protegido y forzar a la gente a usar tu fábrica de "amigos".

...Ok, bueno, francamente, puedes vivir sin él.

Para hacer TDD muchas veces he usado la palabra clave "amigo" en C++.
¿Puede un amigo saber todo sobre mí?

No, es solo una amistad unidireccional :`(

Un caso específico donde uso friend es al crear único clases.El friend La palabra clave me permite crear una función de acceso, que es más concisa que tener siempre un método "GetInstance()" en la clase.

/////////////////////////
// Header file
class MySingleton
{
private:
    // Private c-tor for Singleton pattern
    MySingleton() {}

    friend MySingleton& GetMySingleton();
}

// Accessor function - less verbose than having a "GetInstance()"
//   static function on the class
MySingleton& GetMySingleton();


/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
    static MySingleton theInstance;
    return theInstance;
}

Las funciones y clases amigas brindan acceso directo a miembros privados y protegidos de la clase para evitar romper la encapsulación en el caso general.La mayor parte del uso es con ostream:nos gustaría poder escribir:

Point p;
cout << p;

Sin embargo, esto puede requerir acceso a los datos privados de Point, por lo que definimos el operador sobrecargado

friend ostream& operator<<(ostream& output, const Point& p);

Sin embargo, existen implicaciones obvias de encapsulación.Primero, ahora la clase o función amiga tiene acceso completo a TODOS los miembros de la clase, incluso aquellos que no pertenecen a sus necesidades.En segundo lugar, las implementaciones de la clase y del amigo ahora están entrelazadas hasta el punto en que un cambio interno en la clase puede romper al amigo.

Si ves al amigo como una extensión de la clase, entonces esto no es un problema, lógicamente hablando.Pero, en ese caso, ¿por qué era necesario lanzar al amigo en primer lugar?

Para lograr lo mismo que los "amigos" pretenden lograr, pero sin romper la encapsulación, se puede hacer esto:

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

La encapsulación no está rota, la clase B no tiene acceso a la implementación interna en A, pero el resultado es el mismo que si hubiéramos declarado a B amigo de A.El compilador optimizará las llamadas a funciones, por lo que dará como resultado las mismas instrucciones que el acceso directo.

Creo que usar "amigo" es simplemente un atajo con beneficios discutibles, pero con un costo definido.

En C++, la palabra clave "amigo" es útil para sobrecargar operadores y crear puentes.

1.) Palabra clave amiga en la sobrecarga del operador:
Un ejemplo de sobrecarga de operadores es:Digamos que tenemos una clase "Punto" que tiene dos variables flotantes.
"x" (para la coordenada x) e "y" (para la coordenada y).Ahora tenemos que sobrecargar "<<"(operador de extracción) tal que si llamamos "cout << pointobj" luego imprimirá las coordenadas xey (donde pointobj es un objeto de clase Punto).Para ello tenemos dos opciones:

   1.Overload "operator <<()" function in "ostream" class.
   2.Overload "operator<<()" function in "Point" class.
Ahora la primera opción no es buena porque si necesitamos sobrecargar nuevamente este operador para alguna clase diferente, entonces tendremos que volver a realizar cambios en la clase "ostream".
Por eso la segunda es la mejor opción.Ahora el compilador puede llamar"operator <<()" función:

   1.Using ostream object cout.As: cout.operator<<(Pointobj) (form ostream class).
2.Call without an object.As: operator<<(cout, Pointobj) (from Point class).

Porque hemos implementado la sobrecarga en la clase Point.Entonces, para llamar a esta función sin un objeto tenemos que agregar"friend" palabra clave porque podemos llamar a una función amiga sin un objeto.Ahora la declaración de función será como:
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.) Palabra clave de amigo al hacer un puente:
Supongamos que tenemos que crear una función en la que tenemos que acceder a miembros privados de dos o más clases (generalmente denominada "puente").Como hacer esto:
Para acceder a un miembro privado de una clase, debe ser miembro de esa clase.Ahora, para acceder a un miembro privado de otra clase, cada clase debe declarar esa función como una función amiga.Por ejemplo :Supongamos que hay dos clases A y B.Una función "funcBridge()" desea acceder a un miembro privado de ambas clases.Entonces ambas clases deberían declarar "funcBridge()" como:
friend return_type funcBridge(A &a_obj, B & b_obj);

Creo que esto ayudaría a comprender la palabra clave amigo.

Como referencia para declaracion de amistad dice:

La declaración de amigo aparece en el cuerpo de una clase y otorga a una función u otra clase acceso a privado y protegido miembros de la clase donde aparece la declaración de amigo.

Sólo como recordatorio, hay errores técnicos en algunas de las respuestas que dicen que friend solo puedo visitar protegido miembros.

Al implementar algoritmos de árbol para clases, el código marco que nos dio el profesor tenía la clase de árbol como amiga de la clase de nodo.

Realmente no sirve de nada, aparte de permitirle acceder a una variable miembro sin utilizar una función de configuración.

Puede utilizar la amistad cuando diferentes clases (que no heredan una de la otra) utilizan miembros privados o protegidos de la otra clase.

Los casos de uso típicos de las funciones de amigo son operaciones que son llevado a cabo entre dos clases diferentes que acceden a información privada o protegida miembros de ambos.

de http://www.cplusplus.com/doc/tutorial/inheritance/ .

Puede ver este ejemplo donde un método que no es miembro accede a los miembros privados de una clase.Este método debe declararse en esta misma clase como amigo de la clase.

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

Probablemente me perdí algo de las respuestas anteriores, pero otro concepto importante en la encapsulación es ocultar la implementación.Reducir el acceso a miembros de datos privados (los detalles de implementación de una clase) permite una modificación mucho más sencilla del código más adelante.Si un amigo accede directamente a los datos privados, cualquier cambio en los campos de datos de implementación (datos privados) rompe el código de acceso a esos datos.El uso de métodos de acceso elimina en gran medida esto.Bastante importante, creo.

Puede que esta no sea una situación de caso de uso real, pero puede ayudar a ilustrar el uso de amigo entre clases.

La casa club

class ClubHouse {
public:
    friend class VIPMember; // VIP Members Have Full Access To Class
private:
    unsigned nonMembers_;
    unsigned paidMembers_;
    unsigned vipMembers;

    std::vector<Member> members_;
public:
    ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}

    addMember( const Member& member ) { // ...code }   
    void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
    Amenity getAmenity( unsigned memberID ) { // ...code }

protected:
    void joinVIPEvent( unsigned memberID ) { // ...code }

}; // ClubHouse

La clase de miembros

class Member {
public:
    enum MemberShipType {
        NON_MEMBER_PAID_EVENT,   // Single Event Paid (At Door)
        PAID_MEMBERSHIP,         // Monthly - Yearly Subscription
        VIP_MEMBERSHIP,          // Highest Possible Membership
    }; // MemberShipType

protected:
    MemberShipType type_;
    unsigned id_;
    Amenity amenity_;
public:
    Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
    virtual ~Member(){}
    unsigned getId() const { return id_; }
    MemberShipType getType() const { return type_; }
    virtual void getAmenityFromClubHouse() = 0       
};

class NonMember : public Member {
public:
   explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}   

   void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class PaidMember : public Member {
public:
    explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class VIPMember : public Member {
public:
    friend class ClubHouse;
public:
    explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }

    void attendVIPEvent() {
        ClubHouse::joinVIPEvent( this->id );
    }
};

Comodidades

class Amenity{};

Si observa la relación de estas clases aquí;ClubHouse ofrece una variedad de diferentes tipos de membresías y acceso a membresías.Todos los miembros se derivan de una superclase o clase base, ya que todos comparten una ID y un tipo enumerado que son comunes y las clases externas pueden acceder a sus ID y tipos a través de funciones de acceso que se encuentran en la clase base.

Sin embargo, a través de este tipo de jerarquía de los Miembros y sus clases Derivadas y su relación con la clase ClubHouse, la única clase derivada que tiene "privilegios especiales" es la clase VIPMember.La clase base y las otras 2 clases derivadas no pueden acceder al método joinVIPEvent() de ClubHouse, sin embargo, la clase Miembro VIP tiene ese privilegio como si tuviera acceso completo a ese evento.

Entonces, con VIPMember y ClubHouse hay una vía de acceso de doble sentido donde las otras Clases de miembros son limitadas.

podría adherirse a los principios de programación orientada a objetos más estrictos y puros y garantizar que ningún miembro de datos de ninguna clase tenga siquiera accesores para que todos los objetos debe ser los únicos que puedan conocer sus datos y la única forma de actuar sobre ellos es a través de indirectas mensajes, es decir, métodos.

Pero incluso C# tiene una interno palabra clave de visibilidad y Java tiene su valor predeterminado paquete accesibilidad nivelada para algunas cosas.En realidad, C++ se acerca más al ideal de programación orientada a objetos al minimizar el compromiso de la visibilidad de una clase al especificar exactamente ¿Qué otra clase y solo otras clases podrían verlo.

Realmente no uso C++ pero si C# tuviera amigos Yo quisiera que en lugar de la asamblea global interno modificador, que en realidad uso mucho.Realmente no rompe la encapsulación, porque la unidad de implementación en .NET es una asamblea.

Pero luego está el InternosVisibleParaAtributo (otro ensamblaje) que actúa como un ensamblaje cruzado amigo mecanismo.Microsoft usa esto para visual diseñador Ensambles.

Los amigos también son útiles para devolver llamadas.Podrías implementar devoluciones de llamada como métodos estáticos.

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

dónde callback llamadas localCallback internamente, y el clientData tiene su instancia en él.En mi opinión,

o...

class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}

Lo que esto permite es que el amigo se defina exclusivamente en el cpp como una función de estilo c, y no satura la clase.

De manera similar, un patrón que he visto muy a menudo es poner todos los en realidad miembros privados de una clase en otra clase, que se declara en el encabezado, se define en el cpp y se hace amigo.Esto permite al codificador ocultar gran parte de la complejidad y el funcionamiento interno de la clase al usuario del encabezado.

En el encabezado:

class MyFooPrivate;
class MyFoo
{
    friend class MyFooPrivate;
public:
    MyFoo();
    // Public stuff
private:
    MyFooPrivate _private;
    // Other private members as needed
};

En el PCP,

class MyFooPrivate
{
public:
   MyFoo *owner;
   // Your complexity here
};

MyFoo::MyFoo()
{
    this->_private->owner = this;
}

Se vuelve más fácil ocultar cosas que los de abajo no necesitan ver de esta manera.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top