5

この質問は、私が投稿したこの質問の続きです。

私がやろうとしていたこと:私のポイントは、次の制限付きでA、派生クラスの基本クラスのプライベートメンバーへのアクセスを許可することでした:B

  • 私がアクセスしたいのは構造体です -std::map<>実際には - メソッドではありません。
  • 基本クラスを変更できません。
  • 基本クラスには、バックドアの代替手段としてオーバーロードする可能性のあるテンプレート化されたメソッドがありません。また、2 番目の制限に反するAため、そのようなメソッドは追加しません。

考えられる解決策として、litb の解決策 (投稿/ブログ) を指摘されましたが、私の人生では、これらの投稿で何が行われているかを理解できませんでした。私の問題の解決策を導き出さないでください。

私がやろうとしていること: litbのソリューションからの次のコードは、クラス/構造体からプライベートメンバーにアクセスする方法に関するアプローチを示しており、私が言及した制限をカバーしています。

だから、私はこの1つのコードを再配置しようとしています:

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

// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}

次のことを可能にする何かに-派生クラスの初期化の直後にエントリが追加されるため、クラスを継承しようとしていることに注意してstd::map<>ください。デフォルト値を使用するため、次の特定のインスタンスからアクセスする必要があります。Bstd::map<>AB

// NOT MY CODE -- library <a.h>
class A {
private:
    std::map<int, int> A_map;
};

// MY CODE -- module "b.h"
# include <a.h>
class B : private A {
public:
    inline void uncover() {
        for (auto it(A_map.begin()); it != A_map.end(); ++it) {
            std::cout << it->first << " - " << it->second << std::endl;
        }
    }
};

答えとして欲しいもの:適切な変更を加えた後、上記のコードが機能することを本当に望んいますが、最初のコードブロックで何が行われるかについての説明に非常に満足しています- litbのソリューションからのもの。

4

4 に答える 4

7

残念ながら、ブログの投稿とそのコードは少し不明確です。概念は単純です: 明示的なテンプレートのインスタンス化は、任意のクラスへの無料のバックステージ パスを取得します。

  • ライブラリ クラスの明示的なインスタンス化は、クライアント クラスの実装の詳細である場合があります。
  • 明示的なインスタンス化は、ネームスペース スコープでのみ宣言できます。

このバックステージ パスを配布する自然な方法は、メンバーへのポインターとしてです。特定のクラス メンバーへのポインターがある場合、アクセス資格に関係なく、そのクラスの任意のオブジェクトでアクセスできます。幸いなことに、メンバーへのポインターは、C++03 でもコンパイル時の定数にすることができます。

したがって、明示的にインスタンス化されたときにメンバーへのポインターを生成するクラスが必要です。

明示的なインスタンス化は、クラスを定義する方法にすぎません。クラスを生成するだけでどうすれば何かできるのでしょうか? 次の 2 つの方法があります。

  • friendクラスのメンバーではない関数を定義します。これがlitbの機能です。
  • 起動時に初期化される静的データ メンバーを定義します。これが私のスタイルです。

最初に私のスタイルを紹介し、次にその欠点について説明し、次に litb のメカニズムに合わせて修正します。最終的な結果は、ブログのコードよりも単純です。

シンプルバージョン。

クラスは 3 つのテンプレート引数を取ります: 制限されたメンバーの型、その実際の名前、およびそのポインターを受け取るグローバル変数への参照です。このクラスは、コンストラクターがグローバルを初期化する静的オブジェクトの初期化をスケジュールします。

template< typename type, type value, type & receiver >
class access_bypass {
    static struct mover {
        mover()
            { receiver = value; }
    } m;
};

template< typename type, type value, type & receiver >
typename access_bypass< type, value, receiver >::mover
    access_bypass< type, value, receiver >::m;

使用法:

type_of_private_member target::* backstage_pass;
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* backstage_pass = blah;

それが機能するのを見てください。

main残念ながら、ファイルを初期化する順序をコンパイラに伝える標準的な方法がないため、プログラムが に入る前に、この結果が他のソース ファイルのグローバル オブジェクト コンストラクターで使用可能であることを信頼することはできません。しかし、グローバルは次の順序で初期化されます。それらは宣言されているので、バイパスを先頭に配置するだけで、静的オブジェクトコンストラクターが他のファイルに関数呼び出しを行わない限り問題ありません。

堅牢なバージョン。

これは、タグ構造と関数を追加することで litb のコードから要素を借用していますfriendが、これはマイナーな変更であり、上記よりもひどく悪くはなく、かなり明確なままだと思います。

template< typename type, type value, typename tag >
class access_bypass {
    friend type get( tag )
        { return value; }
};

使用法:

struct backstage_pass {}; // now this is a dummy structure, not an object!
type_of_private_member target::* get( backstage_pass ); // declare fn to call

// Explicitly instantiating the class generates the fn declared above.
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* get( backstage_pass() ) = blah;

それが機能するのを見てください。

この堅牢なバージョンと litb のブログ投稿の主な違いは、すべてのパラメーターを 1 か所に集め、タグ構造を空にしたことです。これは、同じメカニズムへの単純なインターフェイスです。ただし、関数を宣言する必要がありgetます。これは、ブログ コードによって自動的に行われます。

于 2013-02-27T13:34:23.397 に答える
4

さて、あなたはその奇妙な「Rob」コードをユースケースで機能させる方法について尋ねたので、ここにあります。

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

// the class you can't modify
class A {
private:
    std::map<int, int> A_map;
};

struct A_f {
    typedef std::map<int, int> A::*type;
    friend type get(A_f);
};

template struct Rob<A_f, &A::A_map>;

class B : private A {
public:
    inline void uncover() {
        std::map<int, int>::iterator it = (this->*get(A_f())).begin();
    }
};

さて、私は個人的に、ここでの治療法は病気よりも悪いかもしれないと思いますが、私は通常、C++の乱用は大丈夫だと主張する最後の人です。自分で決めることができるので、プリプロセッサを使って昔ながらのやり方でやっている私の答えとは別の答えとしてこれを投稿しました。

編集:

使い方

ここでは、上記のコードを複製しますが、タイプを簡略化し、コードをより多く引き出し、豊富なコメントを付けています。念のために言っておきますが、この演習を行う前はコードをよく理解していませんでした。今は完全には理解していません。また、明日どのように機能するかは確かに覚えていません。警告メンテナ。

プライベートメンバーで変更することを許可されていないコードは次のとおりです。

// we can use any type of value, but int is simple
typedef int value_type;

// this structure holds value securely.  we think.
struct FortKnox {
    FortKnox() : value(0) {}
private:
    value_type value;
};

今、強盗のために:

// define a type which is a pointer to the member we want to steal
typedef value_type FortKnox::* stolen_mem_ptr;

// this guy is sort of dumb, but he knows a backdoor in the standard
template<typename AccompliceTag, stolen_mem_ptr MemPtr>
struct Robber {
    friend stolen_mem_ptr steal(AccompliceTag) {
        return MemPtr; // the only reason we exist: to expose the goods
    }
};

// this guy doesn't know how to get the value, but he has a friend who does
struct Accomplice {
    friend stolen_mem_ptr steal(Accomplice);
};

// explicit instantiation ignores private access specifier on value
// we cannot create an object of this type, because the value is inaccessible
// but we can, thanks to the C++ standard, use this value in this specific way
template struct Robber<Accomplice, &FortKnox::value>;

// here we create something based on secure principles, but which is not secure
class FortKnoxFacade : private FortKnox {
public:
    value_type get_value() const {
        // prepare to steal the value
        // this theft can only be perpetrated by using an accomplice
        stolen_mem_ptr accessor = steal(Accomplice()); // it's over now
        // dereference the pointer-to-member, using this as the target
        return this->*accessor;
    }
};

int main() {
    FortKnoxFacade fort;
    return fort.get_value();
}
于 2013-02-27T11:35:23.603 に答える
3

もっと残忍なことはどうですか?

// MY CODE -- module "b.h"
# define private protected
# include <a.h>
# undef private
class B : private A {
    // now you can access "private" members and methods in A
于 2013-02-27T11:12:08.850 に答える
1

私が知っているこのイディオムの最適なパッケージ バージョンは次のとおりです。

template<class Tag,typename Tag::type MemberPtr>
struct access_cast{
 friend typename Tag::type get(Tag){return MemberPtr;};
};

template<class Tag,class MemberPtr>
struct access_tag{
 typedef MemberPtr type;
 friend type get(Tag);
};

class A {
public:
 auto x() const {return x_;};
private: 
 int x_ = 9;
};

#include <iostream>

struct AMemTag: access_tag<AMemTag,int A::*>{}; //declare tag
template struct access_cast<AMemTag,&A::x_>; //define friend get function

int main() {
 A a;
 std::cout<<a.x()<<"\n";
 a.*get(AMemTag()) = 4; //dereference returned member pointer and modify value
 std::cout<<a.x()<<"\n";
}

それが機能するのを見てください。

于 2015-04-24T19:19:35.780 に答える