作為父類(lèi)的設(shè)計(jì)者,你可能會(huì)躊躇到底應(yīng)該賦予你的成員函數(shù)protected還是private的訪問(wèn)權(quán)限。那么,讓我們來(lái)看看下面幾個(gè)Sample吧:
1、不相關(guān)的類(lèi)訪問(wèn)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的結(jié)果是undefined,但是編譯器一般會(huì)對(duì)A_hack作空派生類(lèi)優(yōu)化,A_hack和A的內(nèi)存布局相同,考試大提示:這里的static_cast通常都是no-op,因此上面的代碼在實(shí)際應(yīng)用中幾乎肯定可以成功。對(duì)于一個(gè)惡意用戶(hù)而言,他的目的已經(jīng)達(dá)到了。
2、調(diào)用純虛函數(shù)
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() {}
};
你覺(jué)得不可能調(diào)用傳說(shuō)中的純虛函數(shù)?你想看看VC中的_purecall到底會(huì)做些什么?試試上面的代碼吧。
問(wèn)題的根源在于父類(lèi)將Fun聲明成了protected。雖然你不應(yīng)當(dāng)在ctor里調(diào)用virtual函數(shù),但是你在ctor調(diào)用Dummy的時(shí)候,并不一定注意到Dummy內(nèi)部會(huì)調(diào)用virtual函數(shù),于是災(zāi)難發(fā)生了
如果你僅僅希望子類(lèi)在virtual函數(shù)中提供某種行為,那么把這些函數(shù)聲明成private吧
盡管上面的代碼都不符合標(biāo)準(zhǔn),但是至少說(shuō)明,你的用戶(hù)可以利用你提供的protected權(quán)限實(shí)現(xiàn)一些你并不希望賦予子類(lèi)的功能。如果你覺(jué)得連private都不放心(譬如邪惡的#define private public,當(dāng)然它違反了One Definition Rule (ODR),因此結(jié)果是不可預(yù)期的),那么你使用PImpl來(lái)實(shí)現(xiàn)你的接口。
1、不相關(guān)的類(lèi)訪問(wèn)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的結(jié)果是undefined,但是編譯器一般會(huì)對(duì)A_hack作空派生類(lèi)優(yōu)化,A_hack和A的內(nèi)存布局相同,考試大提示:這里的static_cast通常都是no-op,因此上面的代碼在實(shí)際應(yīng)用中幾乎肯定可以成功。對(duì)于一個(gè)惡意用戶(hù)而言,他的目的已經(jīng)達(dá)到了。
2、調(diào)用純虛函數(shù)
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() {}
};
你覺(jué)得不可能調(diào)用傳說(shuō)中的純虛函數(shù)?你想看看VC中的_purecall到底會(huì)做些什么?試試上面的代碼吧。
問(wèn)題的根源在于父類(lèi)將Fun聲明成了protected。雖然你不應(yīng)當(dāng)在ctor里調(diào)用virtual函數(shù),但是你在ctor調(diào)用Dummy的時(shí)候,并不一定注意到Dummy內(nèi)部會(huì)調(diào)用virtual函數(shù),于是災(zāi)難發(fā)生了
如果你僅僅希望子類(lèi)在virtual函數(shù)中提供某種行為,那么把這些函數(shù)聲明成private吧
盡管上面的代碼都不符合標(biāo)準(zhǔn),但是至少說(shuō)明,你的用戶(hù)可以利用你提供的protected權(quán)限實(shí)現(xiàn)一些你并不希望賦予子類(lèi)的功能。如果你覺(jué)得連private都不放心(譬如邪惡的#define private public,當(dāng)然它違反了One Definition Rule (ODR),因此結(jié)果是不可預(yù)期的),那么你使用PImpl來(lái)實(shí)現(xiàn)你的接口。

