继承:创建一个类,在已有类的基础上创建新类的过程。目的是为了几个类可以共享一段代码,用继承的方式共享一段代码,可以简化代码,看起来更加简洁。
一、基类和派生类
1,已有类叫基类(父类),新创建的类叫派生类(子类),
2,继承关系的语句格式:
class 派生类名:基类名表
{
数据成员和成员函数说明
}
3,其中,“基类名表”的语句格式为
访问控制基类名1,访问控制 基类名2,………,访问控制 基类名n
注,:
1,访问控制有三种
(1)private 私有继承
(2)public 公有继承
(3)protected 保护继承
但是一般访问控制都用protected
2,只有一个类叫单继承,多个类叫多继承
4,基类的私有成员派生类可以继承但是不能直接使用,需要用公有的成员函数进行访问。
5,基类的公有成员和保护成员派生类可以直接使用,所以在定义基类的成员时,我们一般都是定义为保护成员。
6,例如:求一个物体的体积
#include<bits/stdc++.h>
using namespace std;
class A
{
public:
void getXY()
{
cout<<"enter two numbers of x,y:";
cin>>x>>y;
}
void putXY()
{
cout<<"x="<<x<<""<<"y="<<y<<endl;
}
protected:
int x,y;
};
class B:public A
{
public:
int getS()
{
return s;
}
void makeS()
{
s=x*y;//使用基类数据成员x,y
}
protected:
int s;
};
class C:public B
{
public:
void getH()
{
cout<<"enter a number of h:";
cin>>h;
}
int getV()
{
return v;
}
void makeV()
{
makeS();//使用基类成员函数
v=getS()*h;//使用基类成员函数
}
protected:
int h,v;
};
int main()
{
Aa;
Bb;
Cc;
cout<<"It's a:"<<endl;
a.getXY();
a.putXY();
cout<<"It's b:"<<endl;
b.getXY();//继承类B继承了A类的共有成员函数,所以可以直接调用
b.makeS();
cout<<"S=:"<<b.getS()<<endl;
cout<<"It's c:"<<endl;
c.getXY();
c.getH();
c.makeV();
cout<<"V="<<c.getV()<<endl;
}
说明:继承具有传递性
在例题中,B类继承了A类的公有成员函数,所以B类可以直接调用A类中的成员函数,C类又继承了B 类,同时C类继承的B类中包括了A类中的公有成员函数,所以C类的对象也可以直接调用A类的共有函数
7、访问声名格式:基类名::成员
8、派生类的生成过程
(1)吸收基类成员(不继承构造函数和析构函数)
(2)改造基类成员(同名修改)
(3)添加派生类新成员
二、重名成员——改造基类成员
1、在派生类中使用基类的同名成员,可以显示的使用作用域符指定,格式为:基类名::成员
2、重名数据成员
如果在派生类中定义了与基类相同名字的数据成员,根据继承规则,在建立派生类对象时,系统会分别建立不同的存储空间。
派生类中的与基类同名的数据成员不能在基类中直接访问,访问时应为:基类名::成员
3,例如
#include<bits/stdc++.h>
using namespace std;
class Base
{
public:
int a,b;
};
class Derived:public Base
{
public:
int b,c;
};
int main()
{
Derived d;
d.a=1;
d.Base::b=2;//使用的是基类Base中的数据成员
d.b=3;//使用的是Derived中的数据成员
d.c=4;
cout<<"d.a="<<d.a<<endl;
cout<<"d.Base::b="<<d.Base::b<<endl;
cout<<"d.b="<<d.b<<endl;
cout<<"d.c="<<d.c<<endl;
}
4、重名成员函数
在派生类中定义与基类同名的成员函数,称为在派生类中重载基类的成员函数,由调用形式指示this指针的不同类型,调用不同版本的成员函数。
5,例如
#include<bits/stdc++.h>
using namespace std;
class A
{
public:
int a1,a2;
A(int i1=0,int i2=0)
{
a1=i1;
a2=i2;
}
void print()
{
cout<<"a1="<<a1<<endl;
cout<<"a2="<<a2<<endl;
}
};
class B:public A
{
public:
int b1,b2;
B(int j1=1,int j2=1)
{
b1=j1;
b2=j2;
}
void print()//定义同名函数
{
cout<<"b1="<<b1<<"\t"<<"b2="<<b2<<endl;
}
void printAB()
{
A::print();//派生类对象调用基类版本同名成员函数
print();//派生类对象调用自身的成员函数
}
};
int main()
{
Bb;
b.A::print();
b.printAB();
}
6、派生类中访问静态成员
如果在基类中定义了静态成员,这些静态成员将在整个体系中被共享,根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质。
三、基类的初始化
1,调用基类构造函数进行初始化,其次,调用自身的构造函数完成新成员的初始化。
2,初始化的顺序:
先完成对基类的构造函数的初始化,然后在调用自身的构造函数,在调用自身的构造函数时,先完成对象成员的初始化,后再对自身的数据成员行项初始化
3,初始化时要用初始化列表的形式
注:(1)基类的构造函数初始化基类成员,但是参数由派生类的构造函数进行传递。
(2)如果基类中的构造函数是带参数的,其派生类必须是带参数的构造函数
(3)构造函数先执行基类的,后执行派生类的;但是析构函数正好相反
4,重载和覆盖
重载:一个类中定义多个同名但是参数不同的函数
覆盖:在基类和派生类中定义了几个同名的数据成员,或者成员函数
四、赋值兼容规则
1、赋值兼容规则的特点
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
2、一个派生类对象也是一个基类对象,一个基类对象可派上用场的地方,派生类对象一样可派上用场。反之则不然。
例如:
class Person {…};
class Student : public Person { … };
void eat(Person &p){};
void study(Student &s){};
void main( )
{
Personp;
Students;
eat(p); // OK
eat(s); // OK 学生也要吃饭
study(s);// OK
study(p);// error C2664: “study”: 不能将参数 1 从“Person”转换Student &”
}
注:(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:
class B {…};
class D:private B {…};
Bb1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
四、本章学习心得
1,通过继承,可以简短代码,使代码看起来更清楚
2,继承实际上就是将一个长代码(包含几个类)中的共有的部分提取出来,组成一个新类,这个新类可以被其他的几个类共用。
3,一般情况下,都是先将几个类写出来之后在观察哪几个类有共有的部分,将这些共有的部分提取出来组成一个新类