C++三大特性----“继承”

来源:互联网 发布:中兴软件开发怎么样 编辑:程序博客网 时间:2024/06/10 23:10

    C++作为一门面向对象的语言,其核心便是它的三大特性:封装,继承和多态。

    而其中,继承这一特性的存在最主要的目的是为了实现代码的复用,即当你在构建多个类的时候,你发现它们之间有相当一部分的功能和属性是相同,这时你就可以通过继承这一特性来进行代码的复用,将重复的部分作为公共部分来使用,从而不必在构建每一个类的时候都将相同的部分在写一遍。

继承的定义格式:

       

继承方式与访问限定符之间的关系:

    1.公有继承public:对于公有继承而言,基类中该成员是被哪种访问限定符所修饰的,那么在派生类中依旧是被这种访问限定符所修饰的,对于成员的访问权限并未做任何修改

    2.保护继承protected:对于保护继承而言,除了将基类当中被public访问限定符所修饰的成员变成以protected所修饰的以外,对于其他访问限定符所修饰的成员,并不改变他们的访问权限。

    3.私有继承private:对于私有继承而言,要将基类当中被public以及protected修饰的成员均变成以private所修饰的,原本以private修饰的不做改动。

综上,根据上述继承方式有以下几个注意点:

    1.对于三种继承方式来说,基类中public,protected对应的成员,在派生类中均可进行访问,而对于private成员,虽说一样是被继承下来了,但由于private访问权限,不能在类外被访问,由此,我们可以清楚的看到protected与private这两个访问限定符之间的区别在于能否在派生类中被访问。

    2.对于三种继承方式下来的派生类的对象,除了public继承能访问基类中public修饰的成员以外,派生类的对象是无法访问基类中的成员的。

    3.使用class关键字时,默认的继承方式为private,而以struct为关键字时,默认的继承方式是public

继承机制下构造函数与析构函数的调用:

构造函数:

    构造派生类对象:a.调用派生类的构造函数;b.在派生类构造函数初始化列表处调用基类的构造函数;c.完成基类构造函数;d.返回并完成派生类构造函数剩下部分


析构函数:

    析构派生类对象:a.调用派生类的析构函数;b.执行派生类析构函数的函数体; c.调用基类的析构函数;d.执行基类析构函数函数体后返回



由此,我们可以总结出以下几点:

1.当基类的构造函数是由系统合成,或者是全缺省的构造函数,那么在构造函数不需要做其他事情的时候,派生类的构造函数系统可以自动合成。

2.当基类的构造函数是缺省的构造函数(即需要进行传参时),那么派生类的构造函数必须给出,系统无法合成。


继承的几项规则与特点:
1.派生类与基类是两个不同的作用域;

2.当派生类中存在与基类同名的成员的时候(成员函数是否同名与参数无关),通过派生类的对象进行访问的时候,只能访问派生类中的成员,若要访问基类中的成员,必须加上作用域限定符(同名隐藏);

3.派生类的对象可以对基类对象进行赋值,反之不可;(public继承)

4.基类对象的引用或指针可以指向派生类的对象,反之不可;(public继承)

5.友元函数是不能被继承的(友元函数不是成员函数);

6.基类中的静态成员可以被继承,但无论有多少派生类,该静态成员仅有一份,也就是说基类与派生类共用一份该静态成员;


继承得到的派生类的对象模型:

1.单继承

对象模型:


测试代码:

class A{public:void fun(int a){cout<<"A"<<endl;}public:int _a1;int _a2;};class B:public A{public:void fun(){cout<<"B"<<endl;}public:int _b1;int _b2;};int main(){B b;b._a1=1;b._a2=2;b._b1=3;b._b2=4;return 0;}
测试结果:

查看派生类对象b的内存空间:

2.多继承

对象模型:

测试代码:

class A{public:int _a1;int _a2;};class B{public:int _b1;int _b2;};class C:public A,public B{public:int _c1;int _c2;};int main(){C c;c._a1=1;c._a2=2;c._b1=3;c._b2=4;c._c1=5;c._c2=6;return 0;}

测试结果:

查看派生类c的内存空间:

3.菱形继承


测试代码:

class A{public:int _a;};class B1:public A{public:int _b1;int _b2;};class B2:public A{public:int _b3;int _b4;};class C:public B1,public B2{public:int _c1;int _c2;};int main(){C c;c.B1::_a=1;c._b1=3;c._b2=4;c.B2::_a=5;c._b3=6;c._b4=7;c._c1=8;c._c2=9;return 0;}
测试结果:

查看派生类对象c的内存空间:

4.虚拟继承与菱形虚拟继承

    研究菱形继承后我们发现一个问题,那就是在菱形继承的最终派生类C的对象c当中会将最开始的基类A中的成员继承两份,一份来自对B1的继承,一份来自对B2的继承,而在通过对象访问的时候,会出现访问A中成员的不明确,从而不得不加上作用域限定符来进行访问。

    而对于这个问题,通过菱形虚拟继承可以很好的解决。

    首先我们来看下虚拟继承的对象模型:

测试代码:

class A{public:int _a1;int _a2;};class B:virtual public A{public:int _b1;int _b2;};int main(){B b;b._a1=1;b._a2=2;b._b1=3;b._b2=4;return 0;}

测试结果:

查看派生类对象b的内存空间:


由此可以推出,虚拟继承的对象模型:

偏移量表内容:


菱形虚拟继承:

测试代码:

class A{public:int _a;};class B1:virtual public A{public:int _b1;int _b2;};class B2:virtual public A{public:int _b3;int _b4;};class C:public B1,public B2{public:int _c1;int _c2;};int main(){C c;c._a=1;c._b1=3;c._b2=4;c._b3=6;c._b4=7;c._c1=8;c._c2=9;return 0;}


测试结果:

查看派生类对象c的内存空间:



由此可以发现,虚拟菱形继承中对于A类的成员只继承了一份,而通过偏移量表中保存的偏移量很好的解决了通过C的对象访问A类成员的不明确的问题。





0 0
原创粉丝点击