2017-02-03 4 views
0

次のコードは正しくコンパイルされ、正しく動作し、クラスの保護されたフィールドにアクセスできます。しかし、これはいいことですか?汚い感じて、私は、Javaから来て何を知っていますか:reinterpret_castを悪用してオブジェクトが派生クラスに変換されていない場合は、それを悪用することはできますか?

#include <iostream> 

class Base { 
    public: 
    Base() : _f(42) { 
    } 
    int getF() { return _f; } 
    protected: 
    int _f; 
}; 

class Der : public Base { 
    public: 
    void setF(int f) { _f = f; } 
}; 

int main(int argc, char ** argv) { 
    Base *b = new Base(); 
    std::cout << b->getF() << std::endl; 
    Der *d = reinterpret_cast<Der*>(b); 
    d->setF(37); 
    std::cout << b->getF()<< std::endl; 
} 

そして、私は右だと、これはOKでない場合は、どのような良い方法は、通常はする必要はありませんオブジェクトの内部カプセル化されたデータフィールドを公開しています変更する必要がありますが、テスト時に変更する必要がありますか?インスタンスは他のコンポーネントの内部に深く作られているので、型の変更は簡単ではありません。

+5

私はこの爆発を見ることができませんが、これは未定義の動作です。 – NathanOliver

+1

'reinterpret_cast'で許可されていることのリストについては、[here](http://en.cppreference.com/w/cpp/language/reinterpret_cast)を参照してください。 –

+4

法律を破る良い方法はありません。 –

答えて

4

いいえ、オブジェクトのタイプがDerでない場合、その動作は定義されていません。これは実行時に失敗する可能性があります。コンパイラのオプティマイザがDer*にキャストしているので、実際にはタイプがDerである必要があると仮定している場合は、通常(特に無効なコードクラッシュを行うようにコンパイラに要求する)以外に、実行時にエラーが発生する可能性があります。これに続いて仮想関数を呼び出すと、コンパイラは動的型が既にDerとして知られているので、仮想メソッドのルックアップを最適化することができます。

もし私が正しいとはいえないのであれば、通常は変更する必要はありませんが、テストで変更する必要があるオブジェクトの内部カプセル化データフィールドを公開する良い方法は何ですか?

friendというキーワードが適切と思われます。このキーワードを使用すると、クラスのプライベート(および保護された)メンバをクラス外で使用できます。 Baseクラスは、どのクラスまたは関数がユニットテストするのかを知っている必要があるため、そのクラスまたは関数へのアクセスを許可できます。完全のために

class Base { 
    friend class BaseTester; 
    // ... 
    protected: 
    int _f; 
}; 

class BaseTester { 
    public: 
    static void test() { 
     Base *b = new Base(); 
     b->_f = 37; 
    } 
}; 

int main(int argc, char ** argv) { 
    BaseTester::test(); 
} 

、何らかの理由であなたがBaseを変更できない場合、あなたが虐待可能性がC++のアクセスチェックのいくつかの穴があります。ここでは一つだ:

BaseHackインサイド
#include <iostream> 

class Base { 
    // ... 
    protected: 
    int _f; 
}; 

class BaseHack : public Base { 
    public: 
    static constexpr int Base::*_f = &BaseHack::_f; 
}; 

int main(int argc, char ** argv) { 
    Base *b = new Base(); 
    b->*BaseHack::_f = 37; 
} 

、表現&BaseHack::_fが許可され、それ名称独自のクラスを介してアクセスし、基本クラスの保護されたメンバー、ので。しかし、_fは実際にクラスBaseに定義されているため、そのタイプはint BaseHack::*ではなくint Base::*であり、Baseのメンバーへのアクセスを妨げるルールはありません。