C++和C#及JAVA的继承性差异(2)

首先,让我们来看bar和foo的关系。这其中有趣的是函数f()在bar类里是公有的,而在foo类里面是私有的。显然,如果我们在主程序main()里把变量me声明成foo*的话,程序是不能编译的。但是如果我们把它声明称bar*的话,我们事实上居然调用了foo类的一个私有函数!这一点很有趣。(其实仔细想想,这也在情理之中。函数的访问权限只是在编译的时候有效。编译好之后,运行时就不起作用了。)

不过,这个特点事实上它为程序库设计者提供了一种强制调用者将变量声明成接口指针类型的办法。我们知道,在很多应用和设计模式中,程序员最好将变量声明成接口类型而不是具体的类。更重要的是,有些基于多态性设计的程序库要求调用者必须将变量声明称指针类型。问题是,在这些情况下,作为程序库的设计者,我们有没有一种切实可行的办法确保程序库的调用者总是将变量声明成接口的指针类型呢?有,就是只把相关函数在接口类型中声明成公有,而在所有具体的类中声明成保护或私有。例如,在下面的程序中,程序库的调用者只能将变量声明成vehicle*,而不是具体的truck或car类型。否则他将不能调用任何函数!

abstract class vehicle

{

public: virtual void run() = 0;

};

class truck : public vehicle

{

private: virtual void run() { … }

};

class car : public vehicle

{

private: virtual void run() { … }

};

其次,让我们来看面试题wa中的函数f()。如果,我们只看它直接基类foo::f()的定义而不检查更上层的“祖先”基类bar::f()的定义,我们根本无从知晓f()是个虚拟函数!这告诉我们在C++中,一旦一个函数被声明成虚拟函数,所有子类型中具有相同接口定义的同名函数都自动成为虚拟函数。所以,为了防止混淆和引起不必要的错误,作为程序员,我们在声明子类中的虚拟函数时,最好不应该省略关键字virtual。例如,如果我们声明foo::f()时也使用了关键字virtual,wa类的设计者就不需要检查“祖先”类bar的定义就可以确认f()是个虚拟函数了。(顺便提一句,在实践中,我们可以用类似LINT之类的程序还帮助我们自动检查此类可能引起混淆的细节。)

但是如果foo类中根本没有定义过f()这个函数呢?wa的设计者就必须要检查所有的“祖先”基类才能确认他要加的f() 函数是不是个虚拟函数。(参见以下的程序段。)这也说明了在C++里,一旦某个函数被声明称虚拟函数,它的子子孙孙都被强制成了虚拟函数!

#include <iostream>

class bar

{

public: virtual void f() { std::cout << "I am in bar." << std::endl; }

};

class foo : public bar

{

private: void g(int) { std::cout << "I am in foo." << std::endl; }

};

class wa : public foo

{

public: void f() { std::cout << "I am in wa." << std::endl; }

};

void main()

{

bar* me = new wa;

me->f();

delete me;

return;

}

// 程序输出

// I am in wa.

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwpfwz.html