web-dev-qa-db-ja.com

メンバーポインターを介した保護されたメンバーへのアクセス:それはハッキングですか?

基本クラスからprotectedに指定されたメンバーは、派生クラスのインスタンスからのみアクセスできることを知っています。これは標準の機能であり、スタックオーバーフローで何度も議論されています。

しかし、ユーザーchtz has me me のように、メンバーポインターを使用してこの制限を回避することは可能です。

struct Base { protected: int value; };
struct Derived : Base
{
    void f(Base const& other)
    {
        //int n = other.value; // error: 'int Base::value' is protected within this context
        int n = other.*(&Derived::value); // ok??? why?
        (void) n;
    }
};

コリールのライブデモ

なぜこれが可能ですか、それは実装のどこかで必要な機能またはグリッチまたは標準の文言ですか?


コメントから別の質問が出てきました: if Derived::fは実際のBase で呼び出されますが、未定義の動作ですか?

52
YSC

class member accessexpr.ref (_aclass.amember_)を使用して、を使用してメンバーにアクセスできないという事実access control[class.access] は、他の式を使用してこのメ​​ンバーにアクセスできないようにしません。

式_&Derived::value_ (タイプは_int Base::*_) は完全に標準に準拠しており、valueのメンバーBaseを指定します。次に、式_a_base.*p_ここで、pBaseのメンバーへのポインターであり、_a_base_はBaseのインスタンスでもあります 標準準拠 =。

したがって、標準に準拠するコンパイラは、式other.*(&Derived::value);を定義された動作にする必要があります。メンバvalue of otherにアクセスします。

30
Oliv

ハックですか?

reinterpret_castを使用するのと同様に、これは危険である可能性があり、バグを見つけるのが難しい可能性があります。しかし、それはうまく形成されており、それが機能するかどうかは間違いありません。

類推を明確にするために:reinterpret_castの動作も標準で正確に指定されており、UBなしで使用できます。しかし、reinterpret_castは型システムを迂回し、型システムは理由があります。同様に、メンバートリックへのこのポインターは標準に従って適切に形成されていますが、メンバーのカプセル化を回避し、そのカプセル化は(通常)理由のために存在します(プログラマーがカプセル化を軽率に使用できると思うので、通常言う)。

[それは]実装または標準の文言のどこかでグリッチですか?

いいえ、実装は正しいです。これが、言語が機能するように指定された方法です。

Derivedのメンバー関数は、ベースの保護されたメンバーであるため、明らかに&Derived::valueにアクセスできます。

その操作の結果は、Baseのメンバーへのポインターです。これは、Baseへの参照に適用できます。メンバーのアクセス権限は、メンバーへのポインターには適用されません。メンバーの名前にのみ適用されます。


コメントから別の質問が浮上しました:Derived :: fが実際のBaseで呼び出された場合、それは未定義の動作ですか?

UBではありません。 Baseにはメンバーがいます。

15
eerorika