subclases que poo polimorfismo objeto ejemplos ejemplo composicion codigo clases c++ encapsulation private-members

poo - que es un objeto en c++



¿Puedo acceder a miembros privados desde fuera de la clase sin usar amigos? (23)

Renuncia

Sí, soy plenamente consciente de que lo que estoy preguntando es totalmente estúpido y que cualquiera que desee probar algo así en el código de producción debe ser despedido y / o fusilado. Estoy buscando principalmente para ver si se puede hacer.

Ahora que eso está fuera del camino, ¿hay alguna forma de acceder a los miembros privados de la clase en C ++ desde fuera de la clase? Por ejemplo, ¿hay alguna manera de hacer esto con los desplazamientos de puntero?

(Bienvenido a las técnicas ingenuas y no preparadas para la producción)

Actualizar

Como mencioné en los comentarios, hice esta pregunta porque quería escribir una publicación de blog sobre sobre-encapsulación (y cómo afecta TDD). Quería ver si había una manera de decir "el uso de variables privadas no es una forma 100% confiable de hacer cumplir la encapsulación, incluso en C ++". Al final, decidí enfocarme más en cómo resolver el problema que en por qué era un problema, así que no destaqué algunas de las cosas que mencioné aquí tan prominentemente como lo había planeado, pero aún así dejé un enlace.

En cualquier caso, si alguien está interesado en cómo salió, aquí está: Enemigos del desarrollo impulsado por la prueba, parte I: encapsulación (sugiero leerlo antes de que decida que estoy loco).


A todas las personas que sugieren " #define private public ":

Este tipo de cosas es ilegal . La norma prohíbe definir / indefinir macros que son léxicamente equivalentes a palabras clave de lenguaje reservado. Si bien tu compilador probablemente no se quejará (aún no he visto un compilador que lo haga), no es algo que sea "bueno".


Al hacer referencia a * esto , habilita una puerta trasera para todos los datos privados dentro de un objeto.

class DumbClass { private: int my_private_int; public: DumbClass& backdoor() { return *this; } }


Al lado de #define private public también puede #define private protected y luego definir alguna clase foo como descendiente de la clase deseada para tener acceso a sus métodos (ahora protegidos) a través del tipo de conversión.


Como alternativa al método de la puerta trasera de la plantilla puede usar la clase de puerta trasera de la plantilla. La diferencia es que no necesita poner esta clase de puerta trasera en el área pública de la clase que va a probar. Utilizo el hecho de que muchos compiladores permiten que las clases anidadas accedan al área privada de la clase adjunta (que no es exactamente el estándar 1998 pero se considera que es el comportamiento "correcto"). Y, por supuesto, en C ++ 11 esto se convirtió en comportamiento legal.

Mira este ejemplo:

#include <vector> #include <cassert> #include <iostream> using std::cout; using std::endl; ///////// SystemUnderTest.hpp class SystemUnderTest { //...put this ''Tested'' declaration into private area of a class that you are going to test template<typename T> class Tested; public: SystemUnderTest(int a): a_(a) {} private: friend std::ostream& operator<<(std::ostream& os, const SystemUnderTest& sut) { return os << sut.a_; } int a_; }; /////////TestFramework.hpp class BaseTest { public: virtual void run() = 0; const char* name() const { return name_; } protected: BaseTest(const char* name): name_(name) {} virtual ~BaseTest() {} private: BaseTest(const BaseTest&); BaseTest& operator=(const BaseTest&); const char* name_; }; class TestSuite { typedef std::vector<BaseTest*> Tests; typedef Tests::iterator TIter; public: static TestSuite& instance() { static TestSuite TestSuite; return TestSuite; } void run() { for(TIter iter = tests_.begin(); tests_.end() != iter; ++iter) { BaseTest* test = *iter; cout << "Run test: " << test->name() << endl; test->run(); } } void addTest(BaseTest* test) { assert(test); cout << "Add test: " << test->name() << endl; tests_.push_back(test); } private: std::vector<BaseTest*> tests_; }; #define TEST_CASE(SYSTEM_UNDER_TEST, TEST_NAME) / class TEST_NAME {}; / template<> / class SYSTEM_UNDER_TEST::Tested<TEST_NAME>: public BaseTest / { / Tested(): BaseTest(#SYSTEM_UNDER_TEST "::" #TEST_NAME) / { / TestSuite::instance().addTest(this); / } / void run(); / static Tested instance_; / }; / SYSTEM_UNDER_TEST::Tested<TEST_NAME> SYSTEM_UNDER_TEST::Tested<TEST_NAME>::instance_; / void SYSTEM_UNDER_TEST::Tested<TEST_NAME>::run() //...TestSuiteForSystemUnderTest.hpp TEST_CASE(SystemUnderTest, AccessPrivateValueTest) { SystemUnderTest sut(23); cout << "Changed private data member from " << sut << " to "; sut.a_ = 12; cout << sut << endl; } //...TestRunner.cpp int main() { TestSuite::instance().run(); }


Con bastante frecuencia, una clase proporciona métodos de mutador a datos privados (getters y setters).

Si una clase proporciona un getter que devuelve una referencia constante (pero no establece), entonces puede simplemente const_cast el valor de retorno del getter, y usar eso como un valor l:

class A { private: double _money; public: A(money) : _money(money) {} const double &getMoney() const { return _money; } }; A a(1000.0); const_cast<double &>(a.getMoney()) = 2000.0;


Definitivamente es posible acceder a miembros privados con un desplazamiento de puntero en C ++. Supongamos que tengo la siguiente definición de tipo a la que quería acceder.

class Bar { SomeOtherType _m1; int _m2; };

Suponiendo que no hay métodos virtuales en Bar, el caso fácil es _m1. Los miembros en C ++ se almacenan como desplazamientos de la ubicación de la memoria del objeto. El primer objeto está en offset 0, el segundo objeto en offset de sizeof (primer miembro), etc ...

Así que aquí hay una forma de acceder a _m1.

SomeOtherType& GetM1(Bar* pBar) { return*(reinterpret_cast<SomeOtherType*>(pBar)); }

Ahora _m2 es un poco más difícil. Necesitamos mover el tamaño del puntero original de (SomeOtherType) bytes desde el original. El cast a char es para asegurarme de que estoy incrementando en una compensación de bytes

int& GetM2(Bar* pBar) { char* p = reinterpret_cast<char*>(pBar); p += sizeof(SomeOtherType); return *(reinterpret_cast<int*>(p)); }


El siguiente código accede y modifica a un miembro privado de la clase utilizando un puntero a esa clase.

#include <iostream> using namespace std; class A { int private_var; public: A(){private_var = 0;}//initialized to zero. void print(){cout<<private_var<<endl;} }; int main() { A ob; int *ptr = (int*)&ob; // the pointer to the class is typecast to a integer pointer. (*ptr)++; //private variable now changed to 1. ob.print(); return 0; } /*prints 1. subsequent members can also be accessed by incrementing the pointer (and type casting if necessary).*/


En realidad es bastante fácil:

class jail { int inmate; public: int& escape() { return inmate; } };


Esta respuesta se basa en el concepto exacto demostrado por la respuesta / blog de @ Johannes , ya que parece ser la única forma "legítima". He convertido ese código de ejemplo en una práctica utilidad. Es fácilmente compatible con C ++ 03 (implementando std::remove_reference y reemplazando nullptr ).

Biblioteca

#define CONCATE_(X, Y) X##Y #define CONCATE(X, Y) CONCATE_(X, Y) #define ALLOW_ACCESS(CLASS, TYPE, MEMBER) / template<typename Only, TYPE CLASS::*Member> / struct CONCATE(MEMBER, __LINE__) { friend TYPE (CLASS::*Access(Only*)) { return Member; } }; / template<typename> struct Only_##MEMBER; / template<> struct Only_##MEMBER<CLASS> { friend TYPE (CLASS::*Access(Only_##MEMBER<CLASS>*)); }; / template struct CONCATE(MEMBER, __LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER> #define ACCESS(OBJECT, MEMBER) / (OBJECT).*Access((Only_##MEMBER<std::remove_reference<decltype(OBJECT)>::type>*)nullptr)

API

ALLOW_ACCESS(<class>, <type>, <member>);

Uso

ACCESS(<object>, <member>) = <value>; // 1 auto& ref = ACCESS(<object>, <member>); // 2

Example

struct X { int get_member () const { return member; }; private: int member = 0; }; ALLOW_ACCESS(X, int, member); int main() { X x; ACCESS(x, member) = 42; std::cout << "proof: " << x.get_member() << std::endl; }


He agregado una entrada a mi blog (ver a continuación) que muestra cómo se puede hacer. Aquí hay un ejemplo de cómo lo usa para la siguiente clase

struct A { private: int member; };

Simplemente declare una estructura donde la describa y ejemplifique la clase de implementación utilizada para el robo.

// tag used to access A::member struct A_member { typedef int A::*type; friend type get(A_member); }; template struct Rob<A_member, &A::member>; int main() { A a; a.*get(A_member()) = 42; // write 42 to it std::cout << "proof: " << a.*get(A_member()) << std::endl; }

La plantilla de la clase Rob se define de esta manera, y solo necesita definirse una vez, sin importar a cuántos miembros privados tiene previsto acceder

template<typename Tag, typename Tag::type M> struct Rob { friend typename Tag::type get(Tag) { return M; } };

Sin embargo, esto no muestra que las reglas de acceso de c ++ no sean confiables. Las reglas del lenguaje están diseñadas para protegerse contra errores accidentales: si intenta robar datos de un objeto, el diseño del idioma no le tomará muchas medidas para evitarlo.


He usado otro enfoque (y solución) útil para acceder a un miembro privado / protegido de C ++.
La única condición es que seas capaz de heredar de la clase a la que deseas acceder.
Entonces todo el crédito va a reinterpretar_cast <> () .

Un posible problema es que no funcionará si inserta una función virtual, que modificará la tabla virtual y, por lo tanto, el tamaño / alineación del objeto.

class QObject { Q_OBJECT Q_DECLARE_PRIVATE(QObject) void dumpObjectInfo(); void dumpObjectTree(); ... protected: QScopedPointer<QObjectData> d_ptr; ... } class QObjectWrapper : public QObject { public: void dumpObjectInfo2(); void dumpObjectTree2(); };

Entonces solo necesitas usar la clase de la siguiente manera:

QObject* origin; QObjectWrapper * testAccesor = reinterpret_cast<QObjectWrapper *>(origin); testAccesor->dumpObjectInfo2(); testAccesor->dumpObjectTree2();

Mi problema original era el siguiente: necesitaba una solución que no implique recompilar bibliotecas QT.
Hay dos métodos en QObject , dumpObjectInfo () y dumpObjectTree (), que solo funcionan si las bibliotecas de QT se compilan en modo de depuración y, por supuesto, necesitan acceso al miembro protegido d_ptr (entre otras estructuras internas).
Lo que hice fue utilizar la solución propuesta para reimplementar (con copiar y pegar) esos métodos en dumpObjectInfo2 () y dumpObjectTree2 () en mi propia clase ( QObjectWrapper ) eliminando esas protecciones de preprocesador de depuración.


Hmmm, no sé si esto funcionaría, pero podría valer la pena intentarlo. Cree otra clase con el mismo diseño que el objeto con miembros privados pero con privado cambiado a público. Crea una variable de puntero a esta clase. Utiliza un simple molde para señalar esto a tu objeto con miembros privados e intenta llamar a una función privada.

Esperar chispas y tal vez un choque;)


Lo siguiente es furtivo, ilegal, dependiente del compilador, y puede no funcionar dependiendo de varios detalles de implementación.

#define private public #define class struct

Pero es una respuesta a su OP, en la que invita explícitamente a una técnica que, y cito, es "totalmente estúpida y que cualquiera que desee probar tal cosa en el código de producción debe ser despedido y / o fusilado".

Otra técnica es acceder a datos de miembros privados, mediante la construcción de punteros utilizando desplazamientos codificados a mano / codificados a mano desde el comienzo del objeto.


Si la clase contiene funciones de miembro de plantilla, puede especializar esa función miembro para satisfacer sus necesidades. Incluso si el desarrollador original no pensó en eso.

seguro.h

class safe { int money; public: safe() : money(1000000) { } template <typename T> void backdoor() { // Do some stuff. } };

main.cpp:

#include <safe.h> #include <iostream> class key; template <> void safe::backdoor<key>() { // My specialization. money -= 100000; std::cout << money << "/n"; } int main() { safe s; s.backdoor<key>(); s.backdoor<key>(); }

Salida:

900000 800000


Si puede obtener un puntero a un miembro de una clase, puede usar el puntero sin importar cuáles sean los especificadores de acceso (incluso los métodos).

class X; typedef void (X::*METHOD)(int); class X { private: void test(int) {} public: METHOD getMethod() { return &X::test;} }; int main() { X x; METHOD m = x.getMethod(); X y; (y.*m)(5); }

Por supuesto, mi truco favorito es la puerta trasera de la plantilla de amigos.

class Z { public: template<typename X> void backDoor(X const& p); private: int x; int y; };

Suponiendo que el creador de lo anterior haya definido BackDoor para sus usos normales. Pero desea acceder al objeto y observar las variables de miembros privados. Incluso si la clase anterior se compiló en una biblioteca estática, puede agregar su propia especialización de plantilla para backDoor y así acceder a los miembros.

namespace { // Make this inside an anonymous namespace so // that it does not clash with any real types. class Y{}; } // Now do a template specialization for the method. template<> void Z::backDoor<Y>(Y const& p) { // I now have access to the private members of Z } int main() { Z z; // Your object Z // Use the Y object to carry the payload into the method. z.backDoor(Y()); }


Si sabe cómo su compilador C ++ descifra los nombres, sí.

A menos, supongo, que sea una función virtual. Pero luego, si sabes cómo tu compilador C ++ construye el VTABLE ...

Editar: mirando las otras respuestas, me doy cuenta de que leí mal la pregunta y pensé que se trataba de funciones de miembro, no de datos de miembro. Sin embargo, el punto sigue en pie: si sabes cómo tu compilador establece los datos, entonces puedes acceder a esos datos.


pregunta genial por cierto ... aquí está mi pieza:

using namespace std; class Test { private: int accessInt; string accessString; public: Test(int accessInt,string accessString) { Test::accessInt=accessInt; Test::accessString=accessString; } }; int main(int argnum,char **args) { int x; string xyz; Test obj(1,"Shit... This works!"); x=((int *)(&obj))[0]; xyz=((string *)(&obj))[1]; cout<<x<<endl<<xyz<<endl; return 0; }

Espero que esto ayude.


solo con el propósito del estudio ... intente esto ... puede ser útil, supongo ... este programa puede acceder a los datos privados simplemente conociendo los valores ...

//GEEK MODE....;) #include<iostream.h> #include<conio.h> class A { private :int iData,x; public: void get() //enter the values {cout<<"Enter iData : "; cin>>iData;cout<<"Enter x : ";cin>>x;} void put() //displaying values {cout<<endl<<"sum = "<<iData+x;} }; void hack(); //hacking function void main() {A obj;clrscr(); obj.get();obj.put();hack();obj.put();getch(); } void hack() //hack begins {int hck,*ptr=&hck; cout<<endl<<"Enter value of private data (iData or x) : "; cin>>hck; //enter the value assigned for iData or x for(int i=0;i<5;i++) {ptr++; if(*ptr==hck) {cout<<"Private data hacked...!!!/nChange the value : "; cin>>*ptr;cout<<hck<<" Is chaged to : "<<*ptr; return;} }cout<<"Sorry value not found....."; }


solo crea tu propia función miembro de acceso para extender la clase.


ya que tiene un objeto de clase requerida, supongo que tiene una declaración de clase. Ahora lo que puede hacer es declarar otra clase con los mismos miembros pero mantener todos los especificadores de acceso como públicos.

Por ejemplo, la clase anterior es:

class Iamcompprivate { private: Type1 privateelement1; Typ2 privateelement2; ... public: somefunctions }

puedes declarar una clase como

class NowIampublic { **public:** Type1 privateelement1; Type2 privateelement2; ... somefunctions };

Ahora todo lo que tiene que hacer es Iamcompprivate puntero de la clase Iamcompprivate en un puntero de la clase NowIampublic y usarlos como U wish.

Ejemplo:

NowIampublic * changetopublic(Iamcompprivate *A) { NowIampublic * B = (NowIampublic *)A; return B; }


"El uso de variables privadas no es una forma 100% confiable para aplicar la encapsulación, incluso en C ++". De Verdad? Puede desmontar la biblioteca que necesita, encontrar todos los desplazamientos necesarios y usarlos. Eso te dará la posibilidad de cambiar cualquier miembro privado que te guste ... ¡PERO! No puedes acceder a miembros privados sin algunos hackers sucios. Digamos que escribir const no hará que tu constante sea realmente constante, porque puedes lanzar const lejos o simplemente usar su dirección para invalidarlo. Si está utilizando MSVC ++ y ha especificado "-merge: .rdata = .data" a un enlazador, el truco funcionará sin errores de acceso a la memoria. Incluso podemos decir que escribir aplicaciones en C ++ no es una manera confiable de escribir programas, porque el código resultante de bajo nivel puede ser parchado desde algún lugar afuera cuando se está ejecutando su aplicación. Entonces, ¿cuál es la forma documentada confiable para aplicar la encapsulación? ¿Podemos ocultar los datos en algún lugar de la memoria RAM y evitar que cualquier cosa acceda a ellos, excepto nuestro código? La única idea que tengo es encriptar a los miembros privados y hacer una copia de seguridad de ellos, porque algo puede corromper a esos miembros. Lo siento si mi respuesta es demasiado grosera, no quise ofender a nadie, pero realmente no creo que esa declaración sea sabia.


class A { int a; } class B { public: int b; } union { A a; B b; };

Deberias hacer eso.

ETA: Funcionará para este tipo de clase trivial, pero como algo general, no lo hará.

TC ++ PL Sección C.8.3: "Una clase con una operación de constructor, destructor o de copia no puede ser del tipo de un miembro de la unión ... porque el compilador no sabría qué miembro destruir".

Así que nos queda la mejor apuesta para declarar la class B para que coincida con el diseño de A y para analizar las partes privadas de una clase.


class Test{ int a; alignas(16) int b; int c; }; Test t;

método A: estado de ánimo intrusivo. dado que podemos acceder al código fuente y recompilarlo, podemos usar muchas otras formas como clase de amigo para acceder a un miembro privado, todos son legales de puerta trasera.

método B: humor bruto.

int* ptr_of_member_c = reinterpret_cast<int*>(reinterpret_cast<char*>(&t) + 20);

usamos un número mágico (20), y no siempre es correcto. Cuando el diseño de la prueba de clase cambió, el número mágico es una gran fuente de error.

método C: humor súper hacker. ¿Hay algún estado de ánimo no intrusivo y no bruto? dado que la información de diseño de la prueba de clase está oculta por el compilador, no podemos obtener información de compensación de la boca del cumplidor. ex.

offsetof(Test,c); //complie error. they said can not access private member.

tampoco podemos obtener puntero de miembro de la clase Prueba. ex.

&Test::c ; //complie error. they said can not access private member.

@Johannes Schaub - litb tiene un blog, encontró una manera de robar puntero de miembro privado. pero pensé que esto debería ser un error de Complier o un error de lenguaje. Puedo cumplirlo en gcc4.8, pero no en el compilador vc8.

así que la conclusión puede ser: el propietario construye toda la puerta trasera. el ladrón siempre tiene una manera brutal y mala de entrar. el hacker accidental tiene una forma elegante y automática de entrar.