7

クラス内のプライベート データ メンバーにアクセスしたい。このクラスには、プライベート データ メンバーにアクセスするためのメンバー関数はありません。プライベートです。

私はクラスを取りたいと思っています。1 つの方法は、クラスの宣言をコピーし、private メンバーを public にして、新しいクラス class something_else を呼び出すことでした。次に、再解釈キャストを行い、元のオブジェクトをコピーします。これは機能します。しかし、私はもっとエレガントなものが欲しい...またはおそらく一般的な...または別の方法が必要です。

どのようなオプションがありますか? void* を使用できますか? クラスを別の空のクラスに memcpy できますか? これを行う方法は何ですか??

%

4

6 に答える 6

19

私はそれを仮定しています

  1. あなたはすでに「カプセル化を破るのが悪い」段階を経ています。
  2. 他の可能な解決策を使い果たした、
  3. クラスのヘッダーを変更できません。

GotW #76で示されているように、クラスのプライベート メンバーへのアクセスを無効にする方法はいくつかあります。

  1. friendクラス定義を複製し、宣言を追加します。
  2. #define private public クラスの header をインクルードする前に、悪意のあるマクロを使用してください。
  3. 同一のバイナリ レイアウトでクラス定義を記述しreinterpret_cast、元のクラスから偽のクラスに切り替えるために使用します。
  4. テンプレート メンバー関数がある場合は、それを特殊化します (唯一の移植可能なソリューション)。
于 2009-10-03T08:05:02.793 に答える
3

質問で提案したアイデアでは、元のオブジェクトをコピーする必要はありません。実際のクラス宣言の独自の "all public" バリエーションを記述し、その新しい型へのポインターをキャストすると、それを介してオブジェクトに直接アクセスできます。

これがどれも良いアイデアではない理由は簡単です。ソースを制御していないクラスのオブジェクトを操作する必要があります (そうしないと、必要なアクセス権を与えるためにソースを変更できます)。しかし、あなたがソースを管理していない場合、メンテナーがクラスのレイアウトを変更したらどうなるでしょうか? 複製されたバージョンは一致しなくなり、コンパイラがこの不一致を検出する方法がなくなります。その結果、実行時にメモリが破損する可能性があります。

于 2009-10-03T08:21:32.947 に答える
3

誤解されているので、明確にしなければなりません。以下のすべてのソリューションでは、オブジェクトを再コンパイルする必要はありません。コードでクラスを使用するには、オブジェクト ファイルにコンパイルされている場合、そのクラスの宣言と共にヘッダー ファイルをインクルードする必要があります。

#include <class.h>
ObjectFoo instance;

クラス自体を再コンパイルせずに、ヘッダーを変更する (a) か、ヘッダーを別の場所にコピーしてそのヘッダーを含める (b)こと可能です (ただし、注意しないと危険です) 。

#include <class_fixed.h>
ObjectFoo instance;

新しいヘッダーをインクルードしたコードは、(再コンパイルしていない) オブジェクト ファイル内に として宣言されたクラスの実装があると考えclass_fixed.hます。のように宣言されたクラスが存在しますが、class.h. 新しいヘッダーでメンバーのオフセットを変更 (たとえば、新しいメンバーを追加) すると、コードが正しく機能しなくなります。ただし、アクセスを変更するだけで問題なく動作します。コンパイルされたコードはアクセスについて知りません。これはコンパイル時にのみ問題になります。

これは必ずしも有害ではありません。日常生活では、ライブラリの新しいバージョンをシステムにインストールし、それに依存するすべてのプログラムを再コンパイルしないと、このような変化に遭遇します。しかし、取り扱いには注意が必要です


いくつかの解決策があります。

  1. memcpy()
    しないでください!オブジェクトのコピーは、クラス設計者によって課された特定のポリシーに従う場合があるため、memcpy は使用しないでください。たとえば、auto_ptrsは単に memcopy することはできません。memcopyauto_ptrを実行してから両方に対してデストラクタを実行すると、同じメモリを 2 回解放しようとするため、プログラムがクラッシュします。

  2. ヘッダーまたはマクロを使用して変更private:するライセンスで許可されている場合は、クラスの実装に付属するヘッダー ファイルを編集することで問題を解決できます。実装のソース コード (つまり、クラスの cpp ファイル) が制御下にあるかどうかは問題ではありません。データ メンバー (ヘッダー内) のプライベートをパブリックに変更するだけで十分であり、バイナリが与えられた場合でも問題なく動作します。クラス定義を含む唯一のライブラリ。(メンバー関数の場合、アクセスを変更すると内部名が変更されることがありますが、MSVS と GCC の場合は問題ありません。)public:

  3. 新しいゲッター関数の追加へ の
    変更はほとんどの場合問題ありませんが (クラスにアクセス可能な特定のメンバーがある場合にコンパイルを中断する特定のコンパイル時のチェックに依存しない限り)、新しいゲッター関数の追加は慎重に実行する必要があります。getter 関数はインラインである必要があります(したがって、クラスのヘッダー ファイルで定義されます)。privatepublic

  4. reinterpret_cast
    キャストの時点での実際のインスタンスが特定のコードのクラスから派生できる動的基底クラス (動的とは「仮想関数または基底を使用する」ことを意味します) へのポインターをキャストしない場合、キャストは問題なく機能します。

  5. protected:
    そして忘れてしまった場合に備えて。C++ は members を宣言できprotected:ます。つまり、指定されたから派生したクラスにのみアクセスできます。これはあなたのニーズを満たすかもしれません。

于 2009-10-03T08:23:40.387 に答える
1

できますが、すべきではありません。オブジェクトは単なるメモリです。同じメンバーを持つがすべてがパブリックである同等のクラスにポインターを確実にキャストできます。しかし、なぜこれをしたいのですか?作業する必要がある他の誰かのコードがありますか? 適切なアクセサ メソッドを追加してもらいます。本当にパブリック メンバーとして扱う必要がありますか? クラスを変更します。

何をしようとしているのかよくわかりませんが、おそらく間違いです。

于 2009-10-03T08:05:55.427 に答える
0

ありがとう...元の修正のコードを表示したかったのです。誰かがほのめかした理由は、元のコードを変更できないためです...だから脱獄しなければなりません。


#include<iostream>
using namespace std;

// Class Objectfoo
// Pretend Objectfoo lives somewhere else ... I cannot open him up

class ObjectFoo 
{
  private:
  int datax; 
  public:
   ObjectFoo() { datax = 100; }
   void get() { cout << datax << endl;}
};

// Class ObjectBar
class ObjectBar 
{
  public:
   int datax;
};

ObjectFoo FOOEY;

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar)
{
 bar = reinterpret_cast<ObjectBar*>(foo);
 bar->datax = x;
 return bar;
}

int main() 
{
  ObjectBar* bar;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  cout << "Changing private member " << endl;
  bar = touch_foo(5, &FOOEY, bar);

  cout << "bar->datax = " << bar->datax << endl;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  return 0;
}

これはうまくいきます...しかし、私はもっと一般的なものが欲しいと思います...またはもっと柔軟です。

%

于 2009-10-03T08:51:10.587 に答える
0

「ソースを編集する」というコメントには同意しますが、「プライベート」をコメントアウトするだけでなく、メソッドを追加する必要があると思います。

クラスの宣言が必要なので、おそらくヘッダーはありますが、おそらく .cpp/whatever ファイルはありません。ヘッダーのコピーでインライン メンバー関数をクラスに追加し、元のヘッダーの代わりにこのヘッダーを含めます。アクセスできないソース コードのオブジェクト ファイルにリンクできるはずです。

もちろん、これは言語に組み込まれている保護機能を使用せずにバイパスする悪意のあるハッキングと見なされます。そのため、最小限の悪意のあるハックをお勧めします。すべてを非公開にしないでください。getter (ただし、setter はありません) を使用できる場合は、それを実行してください。もちろん、それを回避する方法が少しでもあるのであれば、本当の最小の悪はそれをしないことです。

これがあなたが作業している他の誰かのクラスである場合、次のバージョンは別の方法で実装されている可能性があり、そのメンバーがまったくない可能性があることに注意してください。

于 2009-10-03T08:18:19.663 に答える