C++继承与派生

1、类的继承与派生

· 保持已有类特性而构造新类的过程称为继承,被继承的类称为 基类(父类)。

· 在已有类的基础上新增自己特性产生的新类的过程称为派生,派生出来的类称为 派生类。

2、访问控制

三种继承方式:

· 共有继承(public)

①基类public和protected成员的访问属性在派生类中保持不变,但在基类的private成员不可访问。

②派生类成员函数可直接访问基类public和protected成员,但不能访问private成员。

③通过派生类对象只能访问基类public成员。

例:共有继承举例

#include<iostream.h>
#include<math.h>
class Point
{
	float X,Y;
public:
	void InitP(float xx=0,float yy=0)
	{X=xx;y=yy;}
	void Move(float xoff,float yoff)
	{X+=xoff;Y+=yoff;}
	float GetX(){return X;}
	float GetY(){return Y;}
}
class Rectangle:public Point	//共有派生类声明
{
	float W,H;					//新增私有函数成员
public:							//新增共有函数成员
	void InitR(float x,float y,float w,float h)
	{InitP(x,y);W=w;H=h}		//访问基类公有成员
	float GetH(){return H;}
	float GetW(){return W;}
}
int main()
{
	Rectangle rect;
	rect.InitR(2,3,20,10);
	rect.Move(3,2);				//派生类对象直接调用基类共有成员函数
	cout<<rect.GetX()<<" "<<rect.GetH()<<endl;//同上
	return 0;
}

· 私有继承

①基类public和protected成员以private身份出现在派生类中,但基类的private成员不可访问。

②派生类成员函数可以直接访问基类的public和protected成员,但不能访问基类的private成员。

③通过派生类的对象不能访问基类中的任何成员。

例:私有继承举例(其余代码同上例)

class Rectangle:private Point	//私有派生类声明
{
	float W,H;					//新增私有函数成员
public:							//新增共有函数成员
	void InitR(float x,float y,float w,float h)
	{InitP(x,y);W=w;H=h}		//访问基类公有成员②
	void Move(float xoff,float yoff)
	{Point::Move(xoff,yoff);}	//对象不能直接访问基类Move,因为Move继承过来变成Private了,只能通过Public调用访问。
	float GetX(){return Point::GetX();}//
	float GetY(){return Point::GetY();}//
	float GetH(){return H;}
	float GetW(){return W;}
}

· 保护继承

①基类public和protected成员都以protected身份出现在派生类,但基类private成员不可访问。

②派生类成员函数可以直接访问基类public和protected成员,但不能访问private成员。

③通过派生类对象不能访问基类的任何成员。

说明:

#include<iostream.h>
class A
{
protected:
	int X;
};
class B:protected A		//私有继承
{
public:
	void Function(int PutX);
	int GetX(){return X;};
};
void B::Function(int PutX)//用函数Function进行对A中X赋值,当然class B也可以是public继承,同样可以操作。
{
	X=PutX;
}
void main()
{
	B b;
	b.Function(5);
	cout<<b.GetX()<<endl;
}

3、基类与派生类的对应关系

· 单继承:派生类只从基类派生;

· 多继承:派生类从多个基类派生;

· 多重派生:由一个基类派生出多个不同的派生类。

· 多层派生:派生类又作为基类,又继续派生新类。


多继承时派生类声明

class 派生类名:继承方式1 基类名1,继承方式2 基类名2,…

{

         成员声明;

}


4、派生类的构造、析构函数

继承时的构造函数

· 基类的构造函数不被继承,需要在派生类中自行声明。

· 声明构造函数时,只需要对本类新增成员初始化,对继承来的基类成员初始化由基类完成。

单一继承时的构造函数

派生类名::派生类名(基类需要的形参,本类需要的形参):基类名(参数)

{

              本类成员初始化赋值语句;

}

多继承时的构造函数

派生类名::派生类名(基类1形参,基类2形参,…基类n形参,本类形参):基类名1(参数),基类名2(参数),…基类名n(参数)

{

              本类成员初始化赋值语句;

}

例:单继承时的构造函数。

#include<iostream.h>
class B
{
	int b;
public:
	B()
	{b=0;}
	B(int i)
	{b=i;}
	~B(){};
	void Print() const
	{cout<<b<<endl;}
};
class C:public B
{
	int c;
public:
	C()
	{c=0;}
	C(int i,int j):B(i)	//构造函数,如果不加:B(i),则i不能传递。
	{c=j;}
	~C(){};
	void Print()const
	{B::Print();		//调用输出
	cout<<c<<endl;}
};
void main()
{
	C obj(5,6);
	obj.Print();
}
析构顺序与构造顺序相反,栈~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

5、同名覆盖原则

· 未强行指明,则通过派生类对象使用的是派生类中同名成员。

· 如要通过派生类对象访问基类中被覆盖的同名成员,应使用基类名限定。

d.B::a=3;

· 二义性

二义性问题举例

例①

class A
{
public:
	void f();
};
class B
{
public:
	void f();
	void g();
};
class C:public A,public B
{
public:
	void g();	//同名覆盖,可以通过B::g()访问。
	void h();       //标记
}
//如果声明 C c1;
//则c1.f();具有二义性,c1.g();无二义性
解决方法:

解决方法一:类名限定c1.A::f() 或 c1.B::f()

解决方法二:在C中声明一个同名成员函数 f(),//标记之后加上一行void f(){A::f();},表明用A类中f()。
例②

class B
{public:int b;}
class B1:public B
{private:int b1;}
class B2:public B
{private:int b2;}
class C:public B1,public B2
{
public:
	intf();
private:
	int d;
}
//有二义性的访问:C c、c.b、c.B::b;因为b被B1和B2继承过,内涵b。
//正确访问:c.B1::b;c.B2::b.

· 虚基类

声明:例:class B1: virtual public B;

作用:主要解决多继承同一基类继承多次产生二义性问题,为最远派生类提供唯一基类成员。

举例:

class B
{public:int b;}
class B1:virtual public B
{private:int b1;}
class B2:virtual public B
{private:int b2;}
class C:public B1,public B2
{private:int d;}
//正确访问:C c; c.b;

虚基类及其派生类构造函数

在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数调用被忽略。

#include<iostream.h>
class B0	//声明基类B0
{
	int nV;
public:
	B0(int n){nV=n;cout<<"B0"<<endl;}
	void fun(){cout<<"B0 fun"<<endl;}
};
class B1:virtual public B0
{
	int nV1;
public:
	B1(int a):B0(a){cout<<"B1"<<endl;}//将不会输出B0,D1调用有效,其余无效
};
class B2:virtual public B0
{
	int nV2;
public:
	B2(int a):B0(a){cout<<"B2"<<endl;}//将不会输出B0,D1调用有效,其余无效
};
class D1:public B1,public B2
{
	int nVd;
public:
	D1(int a):B0(a),B1(a),B2(a)//答案B0,B1,B2,D1,因为最远调用有效。
	{cout<<"D1"<<endl;}
};
void main()
{D1 d1(1);d1.fun();}

· 虚函数

被virtual关键字修饰的成员函数,就是虚函数。

虚函数的作用,用专业术语来解释就是实现多态性,多态性是将接口与实现进行分离;

用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。

被继承的类中同名成员函数也同样是virtual类型的,可以省略不写。

#include<iostream.h>
class B0
{
public:
	virtual void display(){cout<<"B0 display"<<endl;}//虚函数
};
class B1:public B0  //公有继承B0
{
public:
	void display(){cout<<"B1 display"<<endl;}
};
class D1:public B1   //共有继承B1
{
public:
	void display(){cout<<"D1 display"<<endl;}
};
void fun(B0 *ptr)
{ptr->display();} 
void main()
{
	B0 b0;
	B1 b1;
	D1 d1;
	B0 *p;
	p=&b0;
	fun(p);
	p=&b1;
	fun(p);
	p=&d1;
	fun(p);
}
如果没有虚函数,将会输出三个B0 display,有了虚函数才能实现

B0 display

B1 display

D1 display

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值