web-dev-qa-db-fra.com

Puis-je accéder à des membres privés extérieurs à la classe sans utiliser d'amis?

Avertissement

Oui, je suis pleinement conscient du fait que ce que je demande est totalement stupide et que quiconque souhaiterait essayer une telle chose dans le code de production devrait être renvoyé et/ou abattu. Je cherche surtout à voir si peut être fait.

Maintenant que cela n’est plus possible, existe-t-il un moyen d’accéder aux membres de la classe privée en C++ de l’extérieur de la classe? Par exemple, y a-t-il un moyen de faire cela avec des décalages de pointeur?

(Les techniques naïves et non prêtes à la production sont les bienvenues)

Mettre à jour

Comme indiqué dans les commentaires, j'ai posé cette question parce que je voulais rédiger un article de blog sur la surencapsulation (et son incidence sur le TDD). Je voulais voir s'il existait un moyen de dire "l'utilisation de variables privées n'est pas un moyen fiable d'imposer l'encapsulation, même en C++". À la fin, j'ai décidé de me concentrer davantage sur la façon de résoudre le problème plutôt que sur la raison pour laquelle il s'agissait d'un problème. Je n'ai donc pas présenté certains des éléments évoqués ici aussi clairement que prévu, mais j'ai tout de même laissé un lien.

Quoi qu'il en soit, si quelqu'un est intéressé par la façon dont il est sorti, le voici: Ennemis du développement piloté par les tests, partie I: encapsulation (je suggère de le lire avant de décider que je suis fou).

59
Jason Baker

Si la classe contient des fonctions membres de modèle, vous pouvez les personnaliser en fonction de vos besoins. Même si le développeur d'origine n'y a pas pensé.

safe.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>();
}

Sortie:

900000
800000
66
dalle

J'ai ajouté une entrée à mon blog (voir ci-dessous) qui montre comment procéder. Voici un exemple d'utilisation de cette classe pour la classe suivante

struct A {
private:
  int member;
};

Déclarez simplement une structure pour celle-ci où vous la décrivez et instanciez la classe d'implémentation utilisée pour le vol.

// 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;
}

Le modèle de classe Rob est défini comme ceci et ne doit être défini qu'une fois, quel que soit le nombre de membres privés auxquels vous prévoyez d'accéder.

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

Cependant, cela ne montre pas que les règles d'accès de c ++ ne sont pas fiables. Les règles de langage sont conçues pour protéger contre les erreurs accidentelles - si vous essayez de voler les données d'un objet, le langage by-design ne vous prendra pas beaucoup de temps pour vous en empêcher. 

48

Ce qui suit est sournois, illégal, dépendant du compilateur et peut ne pas fonctionner en fonction de divers détails d'implémentation.

#define private public
#define class struct

Mais c’est une réponse à votre OP, dans laquelle vous invitez explicitement une technique qui, je le cite, est "totalement stupide et que quiconque voudrait essayer une telle chose dans le code de production devrait être renvoyé et/ou tiré".


Une autre technique consiste à accéder aux données des membres privés en construisant des pointeurs à l'aide de décalages codés en dur/codés à la main depuis le début de l'objet.

29
ChrisW

Hmmm, je ne sais pas si cela fonctionnerait, mais cela pourrait valoir la peine d'essayer. Créez une autre classe avec la même présentation que l'objet avec des membres privés, mais avec une classe privée modifiée en public. Créez une variable de pointeur sur cette classe. Utilisez un casting simple pour le faire pointer sur votre objet avec des membres privés et essayez d’appeler une fonction privée.

Attendez-vous à des étincelles et peut-être à un crash;)

24
SmacL
class A 
{ 
   int a; 
}
class B
{
   public: 
   int b;
}

union 
{ 
    A a; 
    B b; 
};

Ça devrait le faire.

ETA: Cela fonctionnera pour ce genre de cours trivial, mais en général cela ne fonctionnera pas. 

TC++ PL Section C.8.3: "Une classe avec un constructeur, un destructeur ou une opération de copie ne peut pas être du type d'un membre d'union ... car le compilateur ne sait pas quel membre détruire."

Le meilleur choix est donc de déclarer class B pour qu'il corresponde à celui de A et pour regarder les parties privatives d'une classe.

12
Rob K

Si vous pouvez obtenir un pointeur sur un membre d'une classe, vous pouvez utiliser le pointeur quels que soient les spécificateurs d'accès (même les méthodes).

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);
}

Bien sûr, mon petit hack préféré est le modèle d'ami de la porte arrière.

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

En supposant que le créateur de ce qui précède ait défini backDoor pour ses utilisations normales. Mais vous voulez accéder à l'objet et consulter les variables de membre privé. Même si la classe ci-dessus a été compilée dans une bibliothèque statique, vous pouvez ajouter votre propre spécialisation de modèles pour backDoor et accéder ainsi aux membres.

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());
}
8
Martin York

Il est certainement possible d'accéder aux membres privés avec un décalage de pointeur en C++. Supposons que j'avais la définition de type suivante à laquelle je voulais accéder.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

En supposant qu'il n'y ait pas de méthodes virtuelles dans Bar, le cas facile est _m1. Les membres en C++ sont stockés en tant que décalages de l'emplacement mémoire de l'objet. Le premier objet est à l'offset 0, le deuxième objet à l'offset de sizeof (premier membre), etc ...

Voici donc un moyen d'accéder à _m1.

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

Maintenant, _m2 est un peu plus difficile. Nous devons déplacer les octets sizeof (SomeOtherType) du pointeur d'origine de l'original. La conversion en caractères est destinée à garantir que j'incrémente un décalage d'octet

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

Cette réponse est basée sur le concept exact démontré par @ La réponse de/Johannes Johannes - /, car cela semble être le seul moyen "légitime". J'ai converti cet exemple de code en un utilitaire pratique. Il est facilement compatible avec C++ 03 (en implémentant std::remove_reference et en remplaçant nullptr).

Bibliothèque

#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>);

Usage

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

Exemple

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;
}
4
iammilind

question cool btw ... voici ma pièce:

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;
}

J'espère que cela t'aides.

4
Sushant Mahajan

Si vous savez comment votre compilateur C++ modifie les noms, oui.

Sauf si, je suppose, c'est une fonction virtuelle. Mais alors, si vous savez comment votre compilateur C++ construit le VTABLE ...

Edit: en regardant les autres réponses, je me rends compte que j’ai mal interprété la question et je pensais que c’était à propos des fonctions des membres, pas des données des membres. Cependant, le problème demeure: si vous savez comment votre compilateur dispose les données, vous pouvez accéder à ces données.

3
kdgregory

En guise d'alternative à la méthode de modèle de porte dérobée, vous pouvez utiliser la classe de modèle de porte dérobée. La différence est qu'il n'est pas nécessaire de placer cette classe de porte dérobée dans la zone publique de la classe que vous allez tester. J'utilise le fait que de nombreux compilateurs permettent aux classes imbriquées d'accéder à la zone privée de la classe englobante (ce qui n'est pas exactement le standard de 1998 mais est considéré comme un comportement "correct"). Et bien sûr, dans C++ 11, cela est devenu un comportement légal.

Voir cet exemple:

#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();
}
1
AlexT

C'est en fait assez facile:

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

créez simplement votre propre fonction de membre d'accès pour étendre la classe.

0
RichieHH

Le code suivant accède et modifie un membre privé de la classe à l'aide d'un pointeur sur cette classe. 

#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).*/
0
tanujrastogi

"L'utilisation de variables privées n'est pas une méthode fiable à 100% pour appliquer l'encapsulation, même en C++."Vraiment? Vous pouvez désassembler la bibliothèque dont vous avez besoin, trouver tous les décalages nécessaires et les utiliser . Cela vous donnera la possibilité de modifier tout membre privé que vous aimez ... MAIS! Vous ne pouvez pas accéder aux membres privés sans certains sale hacking .. Disons qu'écrire const ne fera pas que votre constante soit vraiment constante, parce que vous pouvez cast const ou tout simplement utiliser son adresse pour l'invalider. Si vous utilisez MSVC++ et que vous avez spécifié "-merge: .rdata = .data" à un éditeur de liens, l'astuce fonctionnera sans défaut d'accès à la mémoire . Nous pouvons même dire que l'écriture d'applications en C++ n'est pas un moyen fiable de écrire des programmes, car le code de bas niveau obtenu peut être corrigé de l'extérieur lorsque votre application est en cours d'exécution . Alors, quel est le moyen documenté et fiable pour appliquer l'encapsulation? Pouvons-nous cacher les données quelque part dans RAM et empêcher quoi que ce soit d'y accéder, à l'exception de notre code? La seule idée que j'ai est de chiffrer et de sauvegarder des membres privés, car quelque chose peut corrompre ces membres ... Désolé, si ma réponse est trop grossière, je ne voulais offenser personne, mais je ne pense vraiment pas que la déclaration est sage.

0
Dmitriy Yurchenko

but d'étude seulement .... essayez ceci .... peut être utile je suppose ..... ce programme peut accéder aux données privées juste en connaissant les valeurs ...

//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.....";
}
0
NEURON ENIX

J'ai utilisé une autre approche (et solution) utile pour accéder à un membre privé/protégé de c ++.
La seule condition est que vous puissiez hériter de la classe à laquelle vous souhaitez accéder.
Ensuite, tout le crédit revient à reinterpret_cast <> () .

Un problème possible est que cela ne fonctionnera pas si vous insérez une fonction virtuelle, qui modifiera la table virtuelle, et donc la taille/l'alignement de l'objet.

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();
};

Ensuite, il vous suffit d'utiliser la classe comme suit:

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

Mon problème initial était le suivant: j'avais besoin d'une solution qui n'implique pas une recompilation des bibliothèques QT.
Il y a 2 méthodes dans QObject, dumpObjectInfo () et dumpObjectTree (), qui fonctionnent juste si les bibliothèques QT sont compilées en mode débogage, et elles ont bien sûr besoin accès au membre d_ptr proteted (parmi d'autres structures internes).
Ce que j’ai fait est d’utiliser la solution proposée pour réimplémenter (avec copier/coller) ces méthodes dans dumpObjectInfo2 () et dumpObjectTree2 () dans ma propre classe (QObjectWrapper) ces gardes de pré-processeurs de débogage.

0
kikeenrique

puisque vous avez un objet de classe obligatoire, je suppose que vous avez une déclaration de classe . Maintenant, vous pouvez déclarer une autre classe avec les mêmes membres, tout en conservant tous les spécificateurs d'accès public.

Par exemple, la classe précédente est:

class Iamcompprivate
{
private:
    Type1 privateelement1;
    Typ2 privateelement2;
    ...

public:
    somefunctions
}

vous pouvez déclarer une classe comme

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

    somefunctions
};

Il ne vous reste plus qu'à convertir le pointeur de la classe Iamcompprivate en un pointeur de la classe NowIampublic et à les utiliser à votre guise.

Exemple:

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

Très souvent, une classe fournit des méthodes de mutation à des données privées (getters et setters).

Si une classe fournit un getter qui retourne une référence const (mais pas un séparateur), vous pouvez simplement const_cast la valeur de retour du getter et l'utiliser comme une valeur 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;
0
Lanting

Inspiré par @Johannes Schaub - litb, le code suivant peut être un peu plus facile à digérer.

    struct A {
    A(): member(10){}
    private:
    int get_member() { return member;}
    int member;
   };

   typedef int (A::*A_fm_ptr)();
   A_fm_ptr  get_fm();

  template<   A_fm_ptr p> 
  struct Rob{ 
     friend A_fm_ptr  get_fm() {
   return p;
  }
};

 template struct Rob<  &A::get_member>;

 int main() {
   A a;
  A_fm_ptr p = get_fm();

    std::cout << (a.*p)() << std::endl;

  }
0
Yunzhou Wu

A toutes les personnes suggérant " #define private public ":

Ce genre de chose est illégal . La norme interdit de définir/exécuter des macros équivalentes lexicalement à des mots-clés de langage réservé. Bien que votre compilateur ne se plaindra probablement pas (je n'ai pas encore vu de compilateur le faire), ce n'est pas une bonne chose à faire.

0
Tritium

À côté de #define private public vous pouvez aussi #define private protected puis définissez une classe foo en tant que descendant de la classe recherchée pour avoir accès à ses méthodes (désormais protégées) via le transtype. 

0
dmajkic

En faisant référence à * this , vous activez une porte dérobée pour toutes les données privées d’un objet.

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