web-dev-qa-db-ja.com

友達を使わずにクラス外からプライベートメンバーにアクセスできますか?

免責事項

はい、私が質問していることは完全に愚かであり、本番コードでそのようなことを試みたいと思う人は誰でも解雇されなければならないことを完全に承知しています。私は主にができるかどうかを確認しようとしています。

これで邪魔になりましたが、クラス外からC++のプライベートクラスメンバーにアクセスする方法はありますか?たとえば、ポインターオフセットでこれを行う方法はありますか?

(素朴で非生産的な技術は歓迎します)

更新

コメントで述べたように、オーバーカプセル化に関するブログ投稿(およびTDDへの影響)を書きたかったので、この質問をしました。 「プライベート変数を使用することは、C++であってもカプセル化を強制する100%信頼できる方法ではない」と言う方法があったかどうかを見たかったのです。最後に、なぜ問題なのかというよりも、問題を解決する方法にもっと集中することに決めたので、ここで取り上げたもののいくつかを計画どおりに目立たせませんでしたが、それでもリンクを残しました。

とにかく、誰かがそれがどのように出てきたかに興味があるなら、ここにあります: テスト駆動開発の敵パートI:カプセル化 (私がクレイジーだと決める前に読むことをお勧めします)。

63
Jason Baker

クラスにテンプレートメンバー関数が含まれている場合、ニーズに合わせてそのメンバー関数を特化できます。元の開発者がそれを考えていなかったとしても。

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

出力:

900000
800000
68
dalle

私のブログへのエントリ (以下を参照)を追加しました。以下のクラスでどのように使用するかの例を示します

struct A {
private:
  int member;
};

それを記述する構造体を宣言し、強盗に使用される実装クラスをインスタンス化するだけです

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

Robクラステンプレートはこのように定義され、アクセスするプライベートメンバーの数に関係なく、一度だけ定義する必要があります。

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

ただし、これは、c ++のアクセス規則が信頼できないことを示していません。言語規則は、偶発的な間違いから保護するように設計されています-オブジェクトのデータを奪おうとすると、言語by-designは長くはかかりませんあなたを防ぎます。

以下は不正で、違法で、コンパイラに依存しており、さまざまな実装の詳細によっては機能しない場合があります。

#define private public
#define class struct

しかし、それはあなたのOPに対する答えであり、あなたは明示的に「完全に愚かであり、生産コードでそのようなことを試してみたい人は誰でも解雇および/または射撃されるべきです」というテクニックを招待します。


別の手法は、オブジェクトの先頭からハードコーディング/手動コーディングされたオフセットを使用してポインターを構築することにより、プライベートメンバーデータにアクセスすることです。

29
ChrisW

うーん、これがうまくいくかどうかはわかりませんが、試してみる価値はあります。プライベートメンバーを持つオブジェクトと同じレイアウトで、プライベートをパブリックに変更した別のクラスを作成します。このクラスへのポインターの変数を作成します。単純なキャストを使用して、これをプライベートメンバーを持つオブジェクトにポイントし、プライベート関数を呼び出してみてください。

火花と多分クラッシュを期待してください;)

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

union 
{ 
    A a; 
    B b; 
};

それはそれを行う必要があります。

ETA:このような些細なクラスでは機能しますが、一般的には機能しません。

TC++ PLセクションC.8.3:「コンストラクタ、デストラクタ、またはコピー操作を持つクラスは、ユニオンメンバーの型にはなれません...コンパイラは、どのメンバーを破壊するかを知らないためです。」

したがって、Aのレイアウトに一致するようにclass Bを宣言し、クラスのプライベートを調べるためにハックするのが最善策です。

12
Rob K

クラスのメンバーへのポインターを取得できる場合は、アクセス指定子が何であっても(メソッドであっても)ポインターを使用できます。

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

もちろん、私のお気に入りの小さなハックは友人テンプレートの裏口です。

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

上記の作成者が通常の使用のためにbackDoorを定義していると仮定します。ただし、オブジェクトにアクセスして、プライベートメンバー変数を確認する必要があります。上記のクラスが静的ライブラリにコンパイルされている場合でも、backDoorに独自のテンプレート特化を追加して、メンバーにアクセスできます。

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

C++でポインターオフセットを使用してプライベートメンバーにアクセスすることは間違いなく可能です。私がアクセスしたい次の型定義を持っていると仮定しましょう。

class Bar {
  SomeOtherType _m1;
  int _m2;
};

バーに仮想メソッドがないと仮定すると、簡単なケースは_m1です。 C++のメンバーは、オブジェクトのメモリ位置のオフセットとして保存されます。最初のオブジェクトはオフセット0にあり、2番目のオブジェクトはsizeof(first member)のオフセットにあります...

_m1にアクセスする方法は次のとおりです。

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

_m2はもう少し難しいです。元のポインターsizeof(SomeOtherType)バイトを元のポインターから移動する必要があります。 charへのキャストは、バイトオフセットをインクリメントすることを保証することです。

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

この答えは、 @ Johannesのanswer/blog で示される正確な概念に基づいています。これが唯一の「正当な」方法であると思われるためです。このサンプルコードを便利なユーティリティに変換しました。 C++ 03と簡単に互換性があります(std::remove_referencenullptr)を置き換えます。

としょうかん

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

使用法

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

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

クールな質問ところで...ここに私の作品があります:

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

お役に立てれば。

4
Sushant Mahajan

C++コンパイラが名前をマングルする方法を知っているなら、はい。

おそらく、それは仮想機能ではないでしょう。しかし、その後、C++コンパイラがVTABLEをどのように構築するかを知っていれば...

編集:他の回答を見ると、私は質問を読み違えて、それが会員データではなく会員機能に関するものだと思ったことがわかります。ただし、ポイントはまだあります。コンパイラーがデータをどのようにレイアウトするかを知っていれば、そのデータにアクセスできます。

3
kdgregory

テンプレートバックドアメソッドの代替として、テンプレートバックドアクラスを使用できます。違いは、このバックドアクラスを、テストするクラスのパブリックエリアに配置する必要がないことです。私は、多くのコンパイラーがネストされたクラスが囲んでいるクラスのプライベート領域にアクセスすることを許可しているという事実を使用します(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();
}
1
AlexT

実際には非常に簡単です。

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

独自のアクセスメンバー関数を作成して、クラスを拡張するだけです。

0
HörmannHH

次のコードは、そのクラスへのポインターを使用して、クラスのプライベートメンバーにアクセスして変更します。

#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

"プライベート変数を使用することは、C++であってもカプセル化を強制する100%信頼できる方法ではありません。"本当に?必要なライブラリを分解し、必要なすべてのオフセットを見つけて使用できます。これにより、あなたが好きなプライベートメンバーを変更することができます...しかし!いくつかの汚いハッキングなしでプライベートメンバーにアクセスすることはできません。 constと書いても定数が本当に一定にならないとしましょう。'constをキャストしたり、アドレスを使用して無効にしたりできるからです。 MSVC++を使用していて、リンカーに "-merge:.rdata = .data"を指定した場合、メモリアクセスエラーなしでトリックが機能します。 C++でアプリを書くことは、プログラムを書くための信頼できる方法ではないと言うことさえできます。なぜなら、結果の低レベルのコードは、アプリの実行中に外部からパッチされる可能性があるからです。次に、カプセル化を実施するための信頼できる文書化された方法は何ですか? RAMのどこかにデータを隠し、コード以外のデータにアクセスできないようにすることはできますか?プライベートメンバーを暗号化し、バックアップすることです。私の答えが失礼すぎる場合、私は誰かを怒らせるつもりはありませんでしたが、私は本当にその声明が賢明だとは思いません。

0

学習目的のみ....これを試してみてください....役に立つかもしれません.....このプログラムは、値を知るだけでプライベートデータにアクセスできます...

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

別の便利なアプローチ(およびソリューション)を使用して、C++のprivate/protectedメンバーにアクセスしました。
唯一の条件は、アクセスしたいクラスから継承できることです。
その後、すべてのクレジットが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ライブラリの再コンパイルを意味しないソリューションが必要でした。
QObjectdumpObjectInfo()、およびdumpObjectTree()、QTライブラリがデバッグモードでコンパイルされ、d_ptr protetedメンバー(他の内部構造の中で)にアクセスする必要がある場合にのみ機能します。
提案したソリューションを使用して、dumpObjectInfo2()およびdumpObjectTree2()自分のクラス(QObjectWrapper)で、これらのデバッグプリプロセッサガードを削除します。

0
kikeenrique

必要なクラスのオブジェクトがあるので、クラスの宣言があると思います。ここでできることは、同じメンバーで別のクラスを宣言することですが、そこにあるすべてのアクセス指定子をパブリックとして保持します。

たとえば、前のクラスは次のとおりです。

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

public:
    somefunctions
}

クラスを次のように宣言できます

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

    somefunctions
};

あとは、クラスIamcompprivateのポインターをクラスNowIampublicのポインターにキャストし、それらをUの希望通りに使用するだけです。

例:

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

多くの場合、クラスはプライベートデータ(ゲッターとセッター)にミューテーターメソッドを提供します。

クラスがconst参照を返すゲッターを提供する場合(ただし、セッターは提供しない場合)、ゲッターの戻り値を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
Lanting

@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
Yunzhou Wu

#define private public」を提案しているすべての人に:

この種のものは違法です。標準では、予約語キーワードと字句的に同等なマクロの定義/定義解除を禁止しています。あなたのコンパイラーはおそらく文句を言うことはないでしょう(まだコンパイラーを見たことはありません)が、それはやるべきことではありません。

0
Tritium

#define private publicに加えて#define private protectedして、fooクラスを必要なクラスの子孫として定義して、型キャストを介して(現在保護されている)メソッドにアクセスできるようにします。

0
dmajkic

* thisを参照することにより、オブジェクト内のすべてのプライベートデータへのバックドアを有効にします。

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