基类有一个public函数,一个protected函数,一个private函数:
class A {public: voidf() { cout<<'A::f'<<endl; g(); h(); }protected: void g() {cout<<'A::g'<<endl;}private: voidh() {cout<<'A::h'<<endl;}};
派生类public继承基类,没有新增的函数。
class B:public A {};int main(int argc, char *argv[]) { B b; b.f(); return 0;}运行结果:
A::fA::gA::h只覆盖public函数:
class B:public A {public: void f() { cout<<'B::f'<<endl; g();h(); }};编译错误: `void A::h()' is private。
只覆盖protected和private函数:
class B:public A {protected: void g() {cout<<'B::g'<<endl;}private: voidh() {cout<<'B::h'<<endl;}};运行结果:
A::fA::gA::h覆盖所有函数:
class B:public A {public: void f() { cout<<'B::f'<<endl; g(); h(); }protected: void g() {cout<<'B::g'<<endl;}private: voidh() {cout<<'B::h'<<endl;}};运行结果:
B::fB::gB::h
只覆盖public函数:
编译错误: `void A::h()' is private。
只覆盖protected与private函数:
编译错误:`A' is not an accessible base of `B'。
覆盖所有函数:
正常运行,结果与public继承时相同。
与protected情况相同。
C++Primer中对此的总结:
1) public继承:基类成员保持自己的访问级别。
2) protected继承:基类的public和protected成员在派生类中为protected成员。
3) private继承:基类所有成员在派生类中为private成员。
这段文字我理解起来有点头疼,下面是自己对它的理解:
1) C++Primer中提到的,派生类对象中包含之前的所有基类子对象。所以当运行由基类继承而来的函数时,就相当于是派生类对象将请求转发给了基类对象,才会出现当B覆盖了g()和h()后运行f(),结果与直接运行A中的f()相同的情况,因为此时运行的f()就是A中的版本。
2) 派生类从基类继承来的成员中,public成员可被外界访问,protected成员可在类中使用,private成员只能被基类中的函数使用。所以B覆盖了f()后出现h()不可使用的编译错误,因为此时是B中的成员f()试图调用继承来的private成员h(),而不是A中的成员f()。而B在protected继承A,且没有覆盖f()时,无法在main函数中运行b.f(),因为此时f()是B中的protected成员,不能被外界访问。
3) 在没有虚函数时,派生类对象与基类子对象相当于简单的并列关系,是两个独立的个体,且基类子对象中的private函数对派生类对象是封闭的。所以在这种情况下覆盖基类中继承过来的private函数是没有覆盖的意义的,比如B::h(),与A::h()只是单纯的名字相同。
派生类与测试函数:
class B:public A {public: void f() { cout<<'B::f'<<endl; g(); h(); }protected: void g() {cout<<'B::g'<<endl;}private: voidh() {cout<<'B::h'<<endl;}};int main(int argc, char *argv[]) { A*b=new B; b->f(); return 0;}
这两种继承方式时,出现编译错误: `A' is an inaccessible base of `B'。百度了一下原因,自己总结了一下:
A *b=new B这种方式相当于将A当作一个接口,而这两种继承方式下B并没有从A中继承到public成员,也就等于是没有利用到A这个接口,所以编译器拒绝将派生类的指针或引用赋值给A的指针,因为没有意义。
基类:
class A {public: virtual void f() { cout<<'A::f'<<endl; g(); h(); }protected: void g() {cout<<'A::g'<<endl;}private: voidh() {cout<<'A::h'<<endl;}};不覆盖public函数(注释掉B中的f()):
运行结果:
A::fA::gA::h不覆盖protected函数:(注释掉g()):
运行结果:
B::fA::gB::h不覆盖private函数:(注释掉h()):
编译错误: `void A::h()' is private。
不覆盖public函数(注释掉B中的f()):
运行结果:
A::fB::gA::h不覆盖protected函数:(注释掉g()):
运行结果:
A::fA::gA::h不覆盖private函数:(注释掉h()):
编译错误: `void A::h()' is private。
不覆盖public函数(注释掉B中的f()):
运行结果:
A::fA::gB::h不覆盖protected函数:(注释掉g()):
运行结果:
A::fA::gB::h不覆盖private函数:(注释掉h()):
编译错误: `void A::h()' is private。
C++实现虚函数的方式是在类的开头存放一个虚函数表,表中每个函数名项都指向了一个实际的函数地址。派生类如果覆盖了基类中的虚函数,则派生类的虚函数表中此项将指向派生类的实现版本。C++通过这种方法几乎没有额外消耗的实现了运行时多态。
简单的说,A或A的子类中有这么一张表:
f | g | h |
A::f() | A::g() | A::h() |
|
|
|
如果B覆盖了A中的一个函数,比如f(),B中的表就变成了:
f | g | h |
B::f() | A::g() | A::h() |
A::f() |
|
|
这样,即使A *b=new B中a的静态类型为A*,在运行时也能找到正确的函数版本,即B::f()。
而对于B中没有覆盖的函数,比如g()和h(),通过B::f()调用时仍然遵循类继承时的访问原则:派生类中可见基类继承过来的protected成员,不可见继承过来的private成员。因此当B覆盖h()时,B::f()可以成功运行,并调用B::h();但如果B没有覆盖h(),而是从基类中继承过来h(),因为它是private成员,B::f()中不可见,因此编译不通过。
刚开始学习这部分时我成功的被书上的话给绕晕了,以为派生类对象可以访问其它的基类对象的protected成员,导致对下面这种问题迷惑不解:
class A {public: virtual void f(A *a) { cout<<'A::f'<<endl; a->g(this); }protected: virtual void g(A *a) {cout<<'A::g'<<endl;}};class B:public A {public: void f(A *a) { cout<<'B::f'<<endl; a->g(this); }protected: void g(A *a) {cout<<'B::g'<<endl;}};int main(int argc, char *argv[]) { A*b=new B; b->f(b); return 0;}编译错误: `virtual void A::g(A*)' is protected。
摸索了一段时间,才弄清楚原来书上说的派生类对象与基类对象,都指的是派生类对象内部,派生类部分和基类部分,而如果要访问一个独立的基类对象的protected成员是不可能的,这里要遵循类中成员的访问标号原则。
刚才在C++Primer的15.2.2里找到一句话:派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。
以下是我的一些理解:
基类应该把想让用户使用的功能设为public,或者是把所有子类都需要的通用的功能设为public,这样就相当于发挥了接口的作用。
基类应将派生类需要重定义的函数定义为虚函数。如果基类不想让派生类可以调用基类的版本,也就是强制派生类实现自己的版本的话,就将这样的函数设为private,而将允许派生类调用的版本设为protected。
如果派生类只需要利用基类已有的功能,不想实现自己的版本,就不应该重载或实现基类的public函数。
在继承方式上,一般来说都使用public继承。private继承会将基类中的所有成员都设为private,相当于派生类无法使用基类的任何成员和功能,C++Primer中说这种继承的情况非常少见,我也从来没见过这种继承方式。protected继承会将基类中的public成员变成protected,这样就无法利用基类的接口了,也就无法利用到运行时多态的好处,所以一般也是不推荐的。联系客服