2009-10-03 17 views
7

Quiero acceder a un miembro de datos privados en una clase. No hay función de miembro en la clase para acceder al miembro de datos privados. Es privado.¿Puedo transformar un objeto y acceder a los miembros de datos privados en C++?

Quiero tomar la clase y de alguna manera abrirla. Un método consistía en copiar la declaración de la clase, hacer público el miembro privado y llamar a la nueva clase clase something_else. Luego hago una reinterpretación del elenco y copio el objeto original. Esto funciona. Pero quiero algo más elegante ... o tal vez genérico ... o simplemente de otra manera.

¿Qué opciones hay? ¿Puedo usar void *? ¿Puedo memcpy la clase en otra clase vacía? ¿Cuáles son las formas de hacer esto?

%

+3

¿Qué le parece la edición de la fuente? '// private:' funcionará bien;) – LiraNuna

+0

Creo que quiere evitar que los usuarios de su clase entren en problemas (por lo tanto, los privados) y necesita implementar algo que no encaje realmente en el diseño actual. – jdehaan

+0

Creo que no tienes el código, ¿no? –

Respuesta

18

Estoy asumiendo que

  1. que ya ha pasado por "romper la encapsulación es malo" etapa,
  2. otras soluciones posibles agotado,
  3. no pueden cambiar de clase encabezamiento.

Hay algunas maneras de subvertir el acceso a los miembros privados de una clase, como se demuestra en GotW #76.

  1. Duplique una definición de clase y agregue una declaración friend.
  2. Utilice las macros malvadas: #define private publicantes de incluir el encabezado de clase.
  3. Escriba una definición de clase con un diseño binario idéntico y use reinterpret_cast para cambiar de la clase original a una falsa.
  4. Especialice una función de miembro de plantilla si existe (la única solución portátil).
+2

+1. Artículo MARAVILLOSO – LiraNuna

+0

Me encantan las macros malvadas mencionar;) – Calyth

1

Puede, pero no debe. Los objetos son solo memoria. Ciertamente puede convertir el puntero en una clase equivalente que tenga los mismos miembros pero donde todo sea público. Pero, ¿por qué quieres hacer esto? ¿Tiene el código de otra persona con el que necesita trabajar? Haga que agreguen los métodos de acceso adecuados. ¿Realmente necesitas tratarlos como miembros públicos? Cambiar la clase

No estoy muy seguro de lo que estás tratando de hacer, pero es probable que sea un error.

0

Acepto el comentario de "editar la fuente", pero creo que deberías agregar un método, no solo comentar el 'privado'.

Debe tener la declaración de la clase, por lo que presumiblemente tiene el encabezado pero posiblemente no el archivo .cpp/whatever. Agregue una función de miembro en línea a la clase en una copia del encabezado e incluya este encabezado en lugar del original. Debería poder enlazar al archivo objeto para el código fuente inaccesible.

Por supuesto, esto cuenta como un truco del mal, evitando las protecciones integradas en el lenguaje en lugar de trabajar con ellos. Es por eso que sugiero el truco mínimamente malvado: no hagas que todo sea privado, y si puedes salirte con la tuya con un getter (pero sin setter) hazlo. Por supuesto, el verdadero mal mínimo es no hacerlo, si hay alguna manera de evitarlo.

Recuerde, si esta es la clase de alguien más con la que está trabajando, la próxima versión podría implementarse de manera diferente y podría no tener ese miembro en absoluto.

+0

Mi comentario también estaba retratando que hay un problema de diseño en alguna parte ... – LiraNuna

3

Con la idea que sugiere en su pregunta, no necesita copiar el objeto original. Si escribe su propia variación "pública" de la declaración de clase real, y luego lanza un puntero a ese nuevo tipo, puede acceder directamente al objeto a través de ella.

La razón por la cual nada de esto es una buena idea es simple. Debe manipular objetos de una clase de la que no controla la fuente (de lo contrario, podría modificar la fuente para darle el acceso que necesita). Pero si no controlas la fuente, ¿qué sucede si los mantenedores cambian el diseño de su clase? Su versión duplicada ya no coincidirá, y el compilador no podrá detectar esta discrepancia. El resultado probablemente será corrupción de memoria en tiempo de ejecución.

3

Dado que se entiende incorrectamente, tengo que aclarar. Todas las siguientes soluciones no requieren la recompilación del objeto. Para usar una clase en su código, si está compilada en un archivo objeto, debe incluir un archivo de encabezado con la declaración de esa clase.

#include <class.h> 
ObjectFoo instance; 

Se es posible (pero peligroso a menos que usted tiene cuidado) para cambiar la cabecera (a) o copiar la cabecera a otro lugar y que incluyen cabecera (b), sin recompilar la clase en sí .

#include <class_fixed.h> 
ObjectFoo instance; 

su código, en el que incluyó la nueva cabecera se acaba pensar que dentro del archivo de objeto (que no se ha vuelto a compilar!) Se encontrará aplicación de la clase declarada como en class_fixed.h. Si bien persiste la clase declarada como en class.h. Si cambia las compensaciones de los miembros (agregue nuevos miembros, por ejemplo) en su nuevo encabezado, está muerto y el código no funcionará correctamente. Pero solo cambiar el acceso funciona bien. El código compilado no sabe sobre el acceso, esto solo importa en la compilación extraña.

Esto no siempre es perjudicial. En la vida cotidiana se encuentra con un cambio de este tipo cuando instala una nueva versión de una biblioteca en su sistema y no recompila todos los programas que dependen de ella. Pero se debe manejar con cuidado


Existen varias soluciones.

  1. memcpy()
    no lo hacen! No haga falta ya que la copia de objetos a veces se somete a una política específica impuesta por el diseñador de la clase. Por ejemplo, auto_ptrs no se puede simplemente copiar: si copia el auto_ptr y luego se ejecuta el destructor para ambos, intentará liberar la misma memoria dos veces y el programa se bloqueará.

  2. Cambio private:-public: en la cabecera o con macro
    Si su licencia lo permite, puede resolver su problema por editar el archivo de cabecera que viene con la implementación de la clase. Si el código fuente de la implementación (es decircpp-file de la clase) está bajo su control, no importa: cambiar de privado a público para miembros de datos (en el encabezado) es suficiente y funciona bien incluso si se le proporciona una biblioteca de solo binario que contiene una definición de clase. (Para las funciones miembro cambiar el acceso a veces cambia su nombre interno, pero para MSVS y GCC está bien.)

  3. Adición de una nueva función getter
    Si bien el cambio private-public es casi siempre bien (a no ser que se basan en tiempo de compilación específica comprobaciones que deben romper la compilación si la clase tiene cierto miembro accesible), la adición de la nueva función getter se debe realizar con cuidado. La función getter debe estar en línea (y por lo tanto definida en el archivo de encabezado de la clase).

  4. reinterpret_cast
    El reparto funciona bien si NO estás lanzando un puntero a la clase base dinámica (medios dinámicos "con funciones o bases virtuales") cuya instancia real en el momento de la fundición se pueden derivar de la clase en la pieza de código en particular.

  5. protected:
    Y por si acaso se le olvidó. C++ puede declarar miembros protected:, es decir, accesibles solo para las clases derivadas de las dadas. Esto puede satisfacer tus necesidades.

0

Gracias ... Quería mostrar el código de mi solución original. La razón por la que alguien se negó es porque no puedo cambiar el código original ... así que tengo que ir a la cárcel.


#include<iostream> 
using namespace std; 

// Class Objectfoo 
// Pretend Objectfoo lives somewhere else ... I cannot open him up 

class ObjectFoo 
{ 
    private: 
    int datax; 
    public: 
    ObjectFoo() { datax = 100; } 
    void get() { cout << datax << endl;} 
}; 

// Class ObjectBar 
class ObjectBar 
{ 
    public: 
    int datax; 
}; 

ObjectFoo FOOEY; 

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar) 
{ 
bar = reinterpret_cast<ObjectBar*>(foo); 
bar->datax = x; 
return bar; 
} 

int main() 
{ 
    ObjectBar* bar; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    cout << "Changing private member " << endl; 
    bar = touch_foo(5, &FOOEY, bar); 

    cout << "bar->datax = " << bar->datax << endl; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    return 0; 
} 

Esto funciona ... pero creo que quiero algo más genérico ... o más flexible.

%

+0

Muchas respuestas aquí asumieron que no se puede cambiar el código original '.cpp', mientras que se puede cambiar, o simplemente copiar , cambie el nombre y vuelva a incluir el archivo ** header **. Vuelve a leer las respuestas con esta idea. –

+0

Es mucho mejor agregar esta información a la pregunta original en lugar de agregarla como respuesta. – KeithB

Cuestiones relacionadas