is-a and has-a,any more?
作者:tyc611.cublog.cn,2008-11-9
相信任何一本合格的C++教程上都有“is-a”和“has-a”的叙述,却没看到有书提及“is-implemented-in-terms-of”。
如果对C++有一定的了解,应该知道public继承表达的是“is-a”关系,而组合(把另一个类对象作为数据成员)表达的是“has-a”关系,但protected/private继承呢?事实上,proteced/private继承表达的就是“is-implemented-in-terms-of”关系。public继承和protected/private继承的这点区别导致了它们的使用相当的不同:“is-a”关系(B is-a A)表明B对象可以当作A使用,而“is-implemented-in-terms-of”关系(B is-implemented-in-terms-of A)只表明B是根据A实现的,但不能够直接把B当作A来使用。下面举例说明:
class A
{
};
class B: public A
{
};
class C: protected A
{
};
class D: private A
{
};
void func(const A&)
{
}
B b;
func(b); // ok
C c;
func(c); // error, c is "NOT" A!
func((const A&)c); // ok, explicit conversion
D d;
func(d); // error, d is "NOT" A!
func((const A&)d); // ok, explicit conversion
try {
throw B();
}
catch (const A&) {
// ok, the exception will be caught
cout << "catch A" << endl;
}
try {
throw C(); // or throw D();
}
catch (const A&) {
// unlucky, the exception will NOT be caught!
cout << "catch A" << endl;
}
catch (...) {
cout << "catch all" << endl;
}
看到了没,B是A的public派生类,因此可以把B用在需要A的地方,而C和D是A的protected和private派生类,不能够直接用于需要A的地方!但本质上C和D对象仍然是一个A对象,所以可以通过显示类型转换来达到目的。
为什么有此区别呢?下面从语言来深入探讨这个问题。
如果你编译上面的程序,func(c)和func(d)调用将会产生类似编译错误:
error: 'A' is an inaccessible base of 'C'
error: 'A' is an inaccessible base of 'D'
不难理解上面的错误提示,原因是A不是C或D的可访问基类,从而找到了此问题的语言线索。那么,什么是“可访问基类”呢?标准11.2(Accessibility of base classes and base class members)给出了我们需要的答案。
[quote]
11.2.4
...A base class is said to be accessible if an invented public member of the base class is accessible. If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class (4.10, 4.11). [Note: it follows that members and friends of a class X can implicitly convert an X* to a
pointer to a private or protected immediate base class of X. ]...
11.2.3
...a conversion from a pointer to a derived class to a pointer to an inaccessible base class might be ill-formed if an implicit conversion is used, but well-formed if an explicit cast is used. ...
[/quote]
对上面的这段话解释一下:
1. 当基类的public成员可访问时,那么此时基类就是可访问基类。由此,我们可以推出:假如类B继承自类A(不论何种继承),(1)如果是public继承,那么在任何地方A都是B的可访问基类;(2)在类B的成员函数和友元函数中,A也是B的可访问基类(不论何种继承);(3)在类B外,如果是private/protected继承,则A不是可访问基类。
2. 对于可访问基类,可以隐式地把派生类指针或引用转换为基类指针或引用;但对于不可访问基类,
只能通过显示类型转换。
回到前面的例子,由于在func(c)和func(d)调用时,类A是类C和D的不可访问基类,因此不能隐式转换为A,但可以显示转换。
Ok, that's all. Hope you guys will enjoy it!
阅读(2033) | 评论(2) | 转发(0) |