c++ private encapsulation data-integrity access-specifier

C++ ¿Es privado realmente privado?



private encapsulation (9)

Estaba probando la validez del especificador de acceso private en C ++. Aquí va:

Interfaz:

// class_A.h class A { public: void printX(); private: void actualPrintX(); int x; };

Implementación:

// class_A.cpp void A::printX() { actualPrintX(); } void A::actualPrintX() { std::cout << x: }

Construí esto en una biblioteca estática (.a / .lib). Ahora tenemos un par class_A.h y classA.a (o classA.lib). Edité class_A.hy quité el private: de él.

Ahora en otro classTester.cpp:

#include "class_A.h" // the newly edited header int main() { A a; a.x = 12; // both G++ and VC++ allowed this! a.printX(); // allowed, as expected a.actualPrintX(); // allowed by G++, VC++ gave a unresolved linker error return 0; }

Sé que después de manipular el encabezado de una biblioteca, todas las apuestas están desactivadas (me refiero a la integridad del sistema, etc.) Aunque el método es intrépido, ¿está realmente permitido? ¿Hay alguna manera de bloquear esto? ¿O estoy haciendo algo mal aquí?


Dado que nadie ha mencionado una forma de bloquear esto ... Una posible forma de bloquear el acceso a miembros privados, es declararlos como un tipo interno separado no visible fuera del archivo. Por supuesto, si desea proporcionar acceso explícito a este interno, deberá proporcionar la declaración interna. Eso también se hace comúnmente usando un encabezado interno que contiene ese tipo.

Nota: en este ejemplo, deberá realizar un seguimiento de la asignación / liberación de ese objeto interno. Hay otras formas de hacerlo que no requieren esto.

// class_A.h class A { public: void printX(); private: void *Private; }; // class_A.cpp class A_private { void actualPrintX(); int x; }; void A::printX() { reinterpret_cast<A_private *>(Private)->actualPrintX(); } void A_private::actualPrintX() { std::cout << x: }


En la mayoría de los casos, ni siquiera tiene que editar el archivo de encabezado para hacer públicos los miembros privados. Puedes hacer esto con preprocesador. Algo como esto:

//"classWithPrivateMembers.hpp" class C { private: //this is crucial for this method to work static int m; }; int C::m = 12;

y luego, esto funcionará:

#define private public #include "classWithPrivateMembers.hpp" #undef private int main() { C::m = 34; // it works! }


Estoy de acuerdo con la mayoría de las otras respuestas.

Sin embargo, me gustaría señalar que es perfectamente aceptable que un compilador organice físicamente los miembros de manera diferente cuando elimines ese private . Si funciona, eso es suerte. No puedes contar con eso. Si ambas partes no están usando la misma declaración, en realidad no están usando la misma clase.


Lo intenté. Este es un nm de un programa que escribí teniendo una prueba de clase con un método privado y uno público.

0000000100000dcc T __ZN4Test3barEv 0000000100000daa T __ZN4Test3fooEv

Como puedes ver, la firma es exactamente la misma. El enlazador no tiene absolutamente nada para distinguir un método privado de uno público.


No hay implementación de A :: actualPrintX en ninguna parte. Ese es tu error de vinculador.


No. El control de acceso privado está ahí para evitar que USTED haga cosas estúpidas, no como un mecanismo de seguridad para evitar que otros accedan a sus datos o funciones. Hay muchas, muchas formas de superarlo.


También tenga en cuenta que cuando cambia el acceso de una variable miembro, el compilador puede colocarlo en un desplazamiento diferente dentro del objeto de clase. El estándar permite a los compiladores bastante libertad para reorganizar los miembros (creo que al menos dentro del mismo nivel de acceso).


Te has desviado más allá de lo que está permitido en C ++, por lo que lo que estás haciendo no está permitido, pero, por supuesto, puede funcionar en algunos compiladores en algunas situaciones.

Específicamente, estás violando la Regla de Una Definición .

Este article de Herb Sutter lo explica bastante bien: también proporciona una manera legal y portátil de eludir el sistema de especificación de acceso.


private no es un mecanismo de seguridad. Es una forma de comunicar intenciones y ocultar información que otras partes de su programa no necesitan conocer, lo que reduce la complejidad general.

Tener dos archivos de encabezado diferentes no cumple con los estándares, por lo que técnicamente está ingresando a un territorio de comportamiento indefinido, pero prácticamente, como ha descubierto, a la mayoría de los compiladores no les importará.