2011-06-10 10 views
11

¿Es posible declarar una función miembro de una clase de avance declarada como amigo? Estoy tratando de hacer lo siguiente:declarar un miembro de funciones de una clase prospectivas declarada como amigo

class BigComplicatedClass; 

class Storage { 
    int data_; 
public: 
    int data() { return data_; } 
    // OK, but provides too broad access: 
    friend class BigComplicatedClass; 
    // ERROR "invalid use of incomplete type": 
    friend void BigComplicatedClass::ModifyStorage(); 
}; 

Así que el objetivo es (i) restringir la declaración amigo a un solo método, y (ii) no incluir la definición de la clase complicada para reducir el tiempo de compilación.

Un enfoque podría ser añadir una clase que actúa como intermediario:

// In Storage.h: 
class BigComplicatedClass_Helper; 
class Storage { 
    // (...) 
    friend class BigComplicatedClass_Helper; 
}; 

// In BigComplicatedClass.h: 
class BigComplicatedClass_Helper { 
    static int &AccessData(Storage &storage) { return storage.data_; } 
    friend void BigComplicatedClass::ModifyStorage(); 
}; 

Sin embargo, esto parece un poco torpe ... así que supongo que debe haber una mejor solución!

+0

posible duplicado de [Cómo declarar un amigo que es una función miembro de otra clase no se ha definido aún en C++?] (Http://stackoverflow.com/questions/ 4355660/cómo-a-declarar-un-amigo-que-es-un-miembro de la función de-otra-clas de que aún no es definida) –

+0

Gracias por la referencia - vi que se trate; sin embargo, su respuesta aceptada es el acceso de nivel de clase demasiado amplio que quería evitar ... – hrr

Respuesta

11

Como dice @Ben, no es posible, pero puede otorgar acceso específico solo a esa función de miembro a través de "passkey". Funciona un poco como la clase de ayuda intermedio, pero es mi humilde opinión más clara:

// Storage.h 
// forward declare the passkey 
class StorageDataKey; 

class Storage { 
    int data_; 
public: 
    int data() { return data_; } 
    // only functions that can pass the key to this function have access 
    // and get the data as a reference 
    int& data(StorageDataKey const&){ return data_; } 
}; 

// BigComplicatedClass.cpp 
#include "BigComplicatedClass.h" 
#include "Storage.h" 

// define the passkey 
class StorageDataKey{ 
    StorageDataKey(){} // default ctor private 
    StorageDataKey(const StorageDataKey&){} // copy ctor private 

    // grant access to one method 
    friend void BigComplicatedClass::ModifyStorage(); 
}; 

void BigComplicatedClass::ModifyStorage(){ 
    int& data = storage_.data(StorageDataKey()); 
    // ... 
} 
+0

¡Agradable! Es cierto que me tomó un tiempo entender por qué el constructor de copias debe ser privado ... – hrr

+0

@hrr: Bueno, no tiene que ser así, ya que aún puede pasar la clave como referencias si lo desea. – Xeo

+0

Si el constructor de copias fuera público, los usuarios sin privilegios podrían obtener una clave con demasiada facilidad: 'StorageDataKey * key_ptr = 0; La clave StorageDataKey (* key_ptr); 'haciendo que el constructor de copias sea privado parece tener mucho sentido. – hrr

3

No, no se puede declarar funciones miembro individuales como amigos hasta que hayan sido declarados. Solo puedes ser amigo de toda la clase.

0

¿Es posible declarar una función miembro de una clase declarada como amigo?

No. Por supuesto, el compilador no tiene conocimiento de tales funciones de miembro.

2

que puede o no ser relevante aquí, pero es útil recordar que hay un mundo salvaje más allá del alcance de las clases y objetos donde las funciones pueden moverse libremente.

Por ejemplo, hace poco necesarios para cerrar un registro de errores del sistema (Singleton mundial estática) de un gestor de excepciones global basada en un puerto de código de otra persona. El archivo de inclusión normal para mi registro de errores entraba en conflicto con el código del controlador de excepción porque ambos querían incluir "windows.h" por razones que yo no había investigado. Cuando esta y otras preguntas me convenció de que no podía hacer una declaración adelantada de las funciones miembro de mi clase ErrorLog, lo que hice fue envolver las funciones necesarias en una función de alcance mundial como esto:

void WriteUrgentMessageToErrorLog(const char * message) 
{ 
    ErrorLog::LogSimpleMessage(message); 
    ErrorLog::FlushAccumulatedMessagesToDisk(); 
} 

Algunas personas son muy particulares sobre manteniendo la integridad de su estructura de clases a cualquier costo ... y rara vez reconocen que las aplicaciones que usan esas clases se construyen inevitablemente sobre algo que carece de esa estructura. Pero está ahí, y se usa juiciosamente, tiene su lugar.

Dada la edad de esta pregunta, no he mirado profundamente en su relevancia aquí. Todo lo que quería compartir era la opinión de que, a veces, un simple mecanismo de envoltura como este es una alternativa mucho más clara y comprensible a algo que tiene mucha más sutileza y astucia al respecto. La sutileza y la astucia tienden a ser modificadas en una fecha posterior por alguien a quien se le debe agregar que no lo entendió del todo. Antes de darse cuenta, usted tiene un error ...

Cuestiones relacionadas