Могу ли я получить доступ к закрытым членам вне класса, не используя друзей? - PullRequest
62 голосов
/ 08 января 2009

Отказ

Да, я полностью осознаю, что то, о чем я спрашиваю, совершенно глупо, и что любой, кто хотел бы попробовать такое в рабочем коде, должен быть уволен и / или застрелен. В основном я смотрю, можно ли сделать .

Теперь, когда это не так, есть ли способ получить доступ к закрытым членам класса в C ++ извне класса? Например, есть ли способ сделать это со смещением указателя?

(приветствуются наивные и не готовые к производству техники)

Обновление

Как отмечалось в комментариях, я задал этот вопрос, потому что хотел написать пост в блоге о чрезмерной инкапсуляции (и как это влияет на TDD). Я хотел посмотреть, есть ли способ сказать: «Использование частных переменных не является на 100% надежным способом обеспечения инкапсуляции, даже в C ++». В конце я решил больше сосредоточиться на том, как решить проблему, а не на том, почему это проблема, поэтому я не представил некоторые материалы, поднятые здесь, так заметно, как планировал, но я все же оставил ссылку.

Во всяком случае, если кому-то интересно, как это получилось, вот оно: Враги тест-ориентированной разработки, часть I: инкапсуляция (предлагаю прочитать, прежде чем вы решите, что я сумасшедший).

Ответы [ 24 ]

1 голос
/ 08 января 2009

Это на самом деле довольно просто:

class jail {
    int inmate;
public:
    int& escape() { return inmate; }
};
1 голос
/ 08 июля 2013

В качестве альтернативы шаблону backdoor-метода вы можете использовать шаблон backdoor class. Разница в том, что вам не нужно помещать этот бэкдор в публичную область класса, который вы собираетесь тестировать. Я использую тот факт, что многие компиляторы позволяют вложенным классам получать доступ к закрытой области включающего класса (что не совсем соответствует стандарту 1998 года, но считается «правильным» поведением). И, конечно, в C ++ 11 это стало законным поведением.

См. Этот пример:

#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();
}
0 голосов
/ 23 июля 2018

Вдохновленный @Johannes Schaub - litb, следующий код может быть немного легче усваивается.

    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 голосов
/ 08 января 2009

Рядом с # определите приватную публичную , вы также можете # определить приватную защищенную и затем определить некоторый класс foo как потомок требуемого класса, чтобы иметь доступ к его (теперь защищенным) методам через тип Кастинг.

0 голосов
/ 18 сентября 2016

только для учебы попробуйте это .... может быть полезно, я думаю ..... эта программа может получить доступ к личным данным, просто зная значения ...

//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 голосов
/ 11 сентября 2014
class Test{
    int a;
    alignas(16) int b;
    int c;
};

Test t;

метод А: навязчивое настроение. так как мы можем получить доступ к исходному коду и перекомпилировать его, мы можем использовать многие другие, как друзья, получают доступ к приватному члену, все они являются легальным бэкдором.

метод Б: грубое настроение.

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

мы используем магическое число (20), и это не всегда правильно. Когда макет класса Test изменился, магическое число стало источником большой ошибки.

метод C: супер хакерское настроение. Есть ли какое-нибудь ненавязчивое и не грубо настроение? поскольку информация макета класса Test скрыта компилятором, мы не можем получить информацию о смещении из уст компли. ех.

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

мы также не можем получить указатель на член класса Test. ех.

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

@ Йоханнес Шауб - у Литб есть блог, он нашел способ ограбить личный указатель члена. но я подумал, что это должно быть ошибкой комплимента или языковой ловушкой. я могу завершить его на gcc4.8, но не на vc8 complier.

поэтому вывод может быть: Хозяин строит весь черный ход. у вора всегда есть грубый и плохой способ взломать. случайный хакер имеет элегантный и автоматизированный способ взломать.

0 голосов
/ 10 августа 2014

Следующий код получает доступ и изменяет закрытый член класса, используя указатель на этот класс.

#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 голосов
/ 08 апреля 2014

Я использовал другой полезный подход (и решение) для доступа к закрытому / защищенному члену c ++.
Единственное условие - вы можете наследовать от класса, к которому хотите получить доступ.
Тогда весь кредит переходит к reinterpret_cast <> () .

Возможная проблема заключается в том, что она не будет работать, если вы вставите виртуальную функцию, которая изменит виртуальную таблицу и, следовательно, размер / выравнивание объекта.

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

Тогда вам просто нужно использовать класс следующим образом:

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

Моя первоначальная проблема заключалась в следующем: мне нужно решение, которое не подразумевало бы перекомпиляцию библиотек QT.
В QObject , dumpObjectInfo () и dumpObjectTree () есть 2 метода, которые просто работать, если библиотеки QT скомпилированы в режиме отладки, и им, конечно, нужен доступ к защищенному члену d_ptr (среди других внутренних структур).
Я использовал предложенное решение для переопределения (с копированием и вставкой) этих методов в dumpObjectInfo2 () и dumpObjectTree2 () в моем собственном классе ( QObjectWrapper * 1028). *) снятие охранников препроцессора отладки.

0 голосов
/ 16 июля 2013

Довольно часто класс предоставляет методы-мутаторы для приватных данных (геттеры и сеттеры).

Если класс действительно предоставляет метод получения, который возвращает константную ссылку (но не метод установки), тогда вы можете просто const_cast вернуть значение метода получения и использовать его в качестве 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 голосов
/ 03 июля 2013

Ссылаясь на * this , вы включаете бэкдор для всех личных данных в объекте.

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