C++之继承



 继承的主要作用:使代码可以复用,它会在原有类的基础上进行功能扩展。

 有继承关系的两个类叫做基类(父类)和派生类(子类)。派生类继承子类的功能特性,并且会增加自己的功能特性。

  继承的定义:class 派生类继承类型基类,例如: class Derived:public Base


     1.继承的分类:

 

  如图所示,有三种继承方式,我们使用例子说明基类和派生类之间的关系:

  公有继承public:

#include<iostream>
using namespace std;

class Base
{
public:
	int _pub;
protected:
	int _pro;
private:
	int _pri;
};

class Derived:public Base
{
public:
	Derived()
	{
		_pub=1;
	}

};

int main()
{
	Derived d;
	d._pub=1;	
}

上述代码编译成功,说明当派生类公有继承基类时,派生类对象在类中和类外都可以访问基类的public成员。

#include<iostream>
using namespace std;

class Base
{
public:
	int _pub;
protected:
	int _pro;
private:
	int _pri;
};

class Derived:public Base
{
public:
	Derived()
	{
		_pro=1;
	}

};

int main()
{
	Derived d;
	d._pro=2;//编译出错	
}
          上述代码编译出现错误,说明公有继承时,派生类对象只能在类中访问基类的protected成员,而不能在类外访问。
#include<iostream>
using namespace std;

class Base
{
public:
	int _pub;
protected:
	int _pro;
private:
	int _pri;
};

class Derived:public Base
{
public:
	Derived()
	{
		_pri=3;//编译出错
	}

};

int main()
{
	Derived d;
    d._pri=3;//编译出错
}

        上述代码有两处编译出错,说明公有继承时,派生类对象在类中和类外都不能访问基类的private成员。   

        用上述三种代码分别测试保护继承和私有继承后,可以总结出:

       (1)三种继承的共同点是,派生类对象可以在类中访问基类的public,protected成员,而不能访问private成员

       (2)不同点,公有继承时,派生类对象在类外只能访问基类的public成员,保护继承和私有继承时,派生类对象在类外不能访问基类的三种成员。

       (3)三种继承的优先级,public>protected>private .

       (4)公有继承时,如果基类的成员不想被派生类对象在类外访问,但需要在派生类中访问,可以将此成员定义为protected成员。

       (5)使用class时,默认为私有继承,使用struct时,默认为公有继承。       

    2.同名隐藏:

         当基类和派生类中有同名成员时,派生类对象会直接访问派生类的成员,隐藏基类成员。

         当需要访问基类成员时,可以使用  派生类对.象基类::同名成员 进行访问,但是注意一般在继承体系中最好不要使用同名定义。

    3.派生类的对象模型:

      

    4.赋值规则(公有继承public)

        (1)子类对象可以赋值给父类对象,但父类对象不能赋值给子类对象。

        

         (2)同样,父类的指针/引用可以指向子类对象 ,子类的指针/引用不能指向父类对象。

     

    5.

        友元关系不能继承;

        静态成员变量可以继承。

    6.继承中构造函数和析构函数的调用:

       

#include<iostream>
using namespace std;

class Base
{
public:	
	Base()
	{
		cout<<"Base()"<<endl;
	}
	~Base()
	{
		cout<<"~Base()"<<endl;
	}
	int _b;
};

class Derived:public Base
{
public:
	Derived()
	{
		cout<<"Derived()"<<endl;
	}
	~Derived()
	{
		cout<<"~Derived()"<<endl;
	}
	int _d;
};
void Funtest()
{
    Derived d;
    d._d=1;
}

int main()
{
	Funtest();
    return 0;
}

            程序执行结果如下:

            

          由此可知, 当在类外定义派生类对象时,函数体执行顺序顺序为:基类构造函数——>派生类构造函数——>派生类析构函数——>基类析构函数。

          但是,当我们使用反汇编及断点进行调试时,发现与上述结果并不相同。

       总结:当构造函数和析构函数在类中显式定义时,基类和派生类的执行顺序为:

      

     注意:

               (1)当基类中有非缺省构造函数时,派生类必须有构造函数且要在初始化列表处调用基类的构造函数;

               (2)基类定义了带参的构造函数时,派生类必须定义构造函数;

               (3)当派生类中未显式定义构造函数、拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载六个成员函数时,系统会默认合成这六个成员函数;

      7.三种继承

           (1)单继承:一个子类只有一个直接父类,上述例子都为单继承;

                             

           (2)多继承:一个子类有多个直接父类;

          

          (3)菱形继承:

            

                   注意:当D类对象访问B类成员时,无法分清楚访问的是C1,C2类中哪一个的成员,产生二义性,并且在D类中会有两个A类成员,造成数据冗余。

      8.虚继承(virtual)

            目的:解决菱形继承中的二义性问题及数据冗余问题                                                                      

#include<iostream>
using namespace std;

class B
{
public:
	int _b;
};

class C1:virtual public B
{
public:
	int _c1;
};

class C2:virtual public B
{
public:
	int _c2;
};

class D:public C1,public C2
{
public:
	int _d;
};

void FunTest()
{
	D d;
	d._b=1;
	d._c1=2;
	d._c2=3;
	d._d=4;
}

int main()
{
	FunTest();
	cout<<sizeof(D)<<endl;//结果为24(4+8+8+4)
	return 0;
}
                   

   

           结果:由上述分析可知,D类的对象模型已经改变,D类对象在访问B类成员时,根据偏移量访问对应基类成员,解决了二义性问题。

          注意:(1)判断虚拟继承时,虚拟继承的汇编语句中会比普通继承多出一条语句push 1;

                                                         

                     (2)当分别给出B,C1,C2,D的构造函数和析构函数时,D类的大小依旧为24;

                     (3)当误将菱形虚拟继承写为D虚拟继承C1,C2时,无法解决二义性问题。

   总结:

                  1.继承的作用就是代码复用,子类继承父类的功能特性并增加自己的功能特性;

                  2.继承可以分为公有继承、保护继承、私有继承,在此基础上又可以分为单继承、多继承、菱形继承;

                  3.一般实际情况中,都会写为公有继承public;

                  4.class关键字默认的继承方式为私有继承,struct关键字默认的继承方式为公有继承;

                  5.继承体系中,派生类和基类是不同的作用域,最好不要定义为同名成员;

                  6.继承体系中的构造函数、析构函数调用顺序为:派生类构造函数->基类构造函数->派生类析构函数->基类析构函数;

                  7.菱形继承中会产生二义性和数据冗余问题,可以使用虚拟继承解决,但要注意虚拟继承的位置;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值