2017-02-03 45 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; 
} 

如果我是正確的,這是不正常,什麼是一個好辦法揭露的對象通常不需要的內部封裝的數據字段需要修改,但需要在測試中進行更改?該實例在其他組件內部深處創建,因此更改其類型不是微不足道的。

+5

雖然我看不到這個吹起來是未定義的行爲。 – NathanOliver

+1

請參閱[這裏](http://en.cppreference.com/w/cpp/language/reinterpret_cast),列出您可以使用'reinterpret_cast'做的事情。 –

+4

沒有好的辦法來打破法律。 –

回答

4

不,如果假裝某個對象的類型爲Der,那麼該行爲是未定義的。這可能會在運行時失敗,而且很糟糕。除了通常的(特別是要求你的編譯器使無效的代碼崩潰)之外,在運行時可能會失敗的一種方式是,如果你的編譯器的優化器假定你將它轉換爲Der*,它肯定是Der。如果您通過調用虛擬函數來遵循這一點,編譯器可能會認爲由於動態類型已知爲Der,因此可以優化虛擬方法查找。

如果我是正確的,這是不正常,什麼是一個好辦法揭露的對象通常並不需要進行修改的內部封裝的數據字段,但確實需要在測試中被改變?

friend關鍵字似乎適合這一點。該關鍵字使課程外部的私人(和受保護)成員可用。由於你的類應該知道哪個類或函數將對它進行單元測試,因此它可以授予對該類或函數的訪問權限。

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

爲了完整,也有C++的訪問檢查了幾個洞,如果由於某種原因,你不能修改Base,你可以濫用。這裏有一個:

#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,表達&BaseHack::_f是允許的,因爲它的名字一個基類,通過自身的訪問類的保護成員。但是因爲_f實際上是在類Base中定義的,所以它的類型是int Base::*而不是int BaseHack::*,並且沒有規則阻止它被用於訪問Base的成員。

相關問題