打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C中继承方式与访问标号的学习笔记

C++中继承方式与访问标号的学习笔记

1     无虚函数的情况

基类有一个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;}};

1.1  第一种情况:public继承

派生类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

1.2  第二种情况:protected继承

只覆盖public函数:

编译错误: `void A::h()' is private。

 

只覆盖protected与private函数:

编译错误:`A' is not an accessible base of `B'。

 

覆盖所有函数:

正常运行,结果与public继承时相同。

1.3  第三种情况:private继承

与protected情况相同。

1.4  本节总结

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()只是单纯的名字相同。

2     有虚函数的情况

派生类与测试函数:

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;}

2.1  第一种情况:protected与private继承

这两种继承方式时,出现编译错误: `A' is an inaccessible base of `B'。百度了一下原因,自己总结了一下:

A *b=new B这种方式相当于将A当作一个接口,而这两种继承方式下B并没有从A中继承到public成员,也就等于是没有利用到A这个接口,所以编译器拒绝将派生类的指针或引用赋值给A的指针,因为没有意义。

2.2  第二种情况:public函数为虚函数

基类:

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。

2.3  第三种情况:protected函数为虚函数

不覆盖public函数(注释掉B中的f()):

运行结果:

A::fB::gA::h
不覆盖protected函数:(注释掉g()):

运行结果:

A::fA::gA::h
不覆盖private函数:(注释掉h()):

编译错误: `void A::h()' is private。

2.4  第四种情况:private函数为虚函数

不覆盖public函数(注释掉B中的f()):

运行结果:

A::fA::gB::h
不覆盖protected函数:(注释掉g()):

运行结果:

A::fA::gB::h
不覆盖private函数:(注释掉h()):

编译错误: `void A::h()' is private。

2.5  本节总结

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()中不可见,因此编译不通过。

3     访问同类型的其它对象的情况

刚开始学习这部分时我成功的被书上的话给绕晕了,以为派生类对象可以访问其它的基类对象的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成员没有特殊访问权限。


4     怎么选择继承方式与访问标号

以下是我的一些理解:

基类应该把想让用户使用的功能设为public,或者是把所有子类都需要的通用的功能设为public,这样就相当于发挥了接口的作用。

基类应将派生类需要重定义的函数定义为虚函数。如果基类不想让派生类可以调用基类的版本,也就是强制派生类实现自己的版本的话,就将这样的函数设为private,而将允许派生类调用的版本设为protected。

如果派生类只需要利用基类已有的功能,不想实现自己的版本,就不应该重载或实现基类的public函数。

在继承方式上,一般来说都使用public继承。private继承会将基类中的所有成员都设为private,相当于派生类无法使用基类的任何成员和功能,C++Primer中说这种继承的情况非常少见,我也从来没见过这种继承方式。protected继承会将基类中的public成员变成protected,这样就无法利用基类的接口了,也就无法利用到运行时多态的好处,所以一般也是不推荐的。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
03、C++ 类访问修饰符
c 之三种继承方式的学习总结
c ++中继承问题小结(转)
5分钟掌握C 中的三种继承方式
浅谈C++中的友元关系
C++ 类访问控制public/private/protected探讨
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服