C++實例ProtectedorPrivate?

字號:

作為父類的設計者,你可能會躊躇到底應該賦予你的成員函數protected還是private的訪問權限。那么,讓我們來看看下面幾個Sample吧:
    1、不相關的類訪問protected成員
    #include
    class A
    {
    protected:
    void b() {printf("Oops!\n");}
    };
    void f(A* a)
    {
    class A_hack:public A
    {
    friend void f(A*);
    };
    static_cast(a)->b();
    }
    class B
    {
    public:
    void f(A* a)
    {
    class A_hack:public A
    {
    friend B;
    };
    static_cast(a)->b();
    }
    };
    int main()
    {
    f(NULL);
    B().f(NULL);
    }
    盡管static_cast的結果是undefined,但是編譯器一般會對A_hack作空派生類優(yōu)化,A_hack和A的內存布局相同,考試大提示:這里的static_cast通常都是no-op,因此上面的代碼在實際應用中幾乎肯定可以成功。對于一個惡意用戶而言,他的目的已經達到了。
    2、調用純虛函數
    class A
    {
    protected:
    virtual void Fun() =0;
    };
    class B:public A
    {
    public:
    B() {Dummy();}
    private:
    void Dummy() {Fun();}
    };
    class C:public B
    {
    public:
    virtual void Fun() {}
    };
    你覺得不可能調用傳說中的純虛函數?你想看看VC中的_purecall到底會做些什么?試試上面的代碼吧。
    問題的根源在于父類將Fun聲明成了protected。雖然你不應當在ctor里調用virtual函數,但是你在ctor調用Dummy的時候,并不一定注意到Dummy內部會調用virtual函數,于是災難發(fā)生了
    如果你僅僅希望子類在virtual函數中提供某種行為,那么把這些函數聲明成private吧
    盡管上面的代碼都不符合標準,但是至少說明,你的用戶可以利用你提供的protected權限實現一些你并不希望賦予子類的功能。如果你覺得連private都不放心(譬如邪惡的#define private public,當然它違反了One Definition Rule (ODR),因此結果是不可預期的),那么你使用PImpl來實現你的接口。