C++中一个类从另一个类获取成员变量和成员函数的过程是继承
被继承的类被称为父类或基类
继承的类被称为子类或派生类
一.简要介绍
当创建的类有若干成员变量和成员函数相似时,可以采用继承
如:
对于学生和老师,都有年龄、性别、职业、名字,分别定义就比较麻烦,就可以定义一个人类,人类有这些属性
class People{ protected: int age; char sex; char* job; char* name; public: void show(){ cout<<name<<"的工作是:"<<job<<endl; } }
就可以用学生类和老师类来继承它
class Student:public People{ } class Teacher:public People{ }
二.继承方式
上面使用了public继承方式 C++继承方式有 public,protected,private这三种,可访问级别由低升高,不同的继承方式
以不同的继承方式继承会影响基类成员在派生类中的访问权限
private:
基类中protected、public成员在派生类中为private
protect:
基类中public成员在派生类中为protect
public:
无影响
也就是说,基类成员访问权限高于继承方式的在派生类中访问权限变为继承方式指定的权限
另外private成员始终无法使用
改变访问权限:
在派生类中使用using修改可访问成员的访问权限
class Teacher:public People{ public: using People::age;//将protected修改为public private: using People::show; //将public修改为private }
三.类构造器的继承
c++类的构造器不能被继承
基类和派生类名字不一样,构造器继承也不太方便
然而基类中的private成员派生类不能访问,所以便有了一个方法:在派生类中调用基类的构造器。相当于派生类构造器用了基类的构造器,而且还可以增加派生类需要的操作。
class People{ int age; char* name; public: People(int age,char* name){ this->age=age; this->name=name; } } class Student:public People{ double score; public: Student(int age,char* name,double score):People(age,name){ this->score=score;//用People的构造器的同时还添加了自己的分数 } }
自动化传参列表:
以上述代码为例
class People{ int age; char* name; public: People(int age1,char* name1):age(age1),name(name1){} }
这样就可以直接把age1赋值给age,name1赋值给name
在这里不是必要的,以下几种情况需要用自动化传参列表
-
对象成员的初始化
-
const修饰的成员,引用成员的初始化
-
派生类调用基类的构造函数初始化基类成员
都有一个特点就是应该在创建出来的时候就要初始化,但是在这里都没能初始化,而且不能再赋值,就可以使用自动化传参列表
四.多继承
c++可以同时继承多个基类,比如一张木桌子,他可以继承木头基类和桌子基类,那么派生类就可以同时用两个基类的成员
但是需要注意的是如果两个基类中有同名的成员,调用时必须要声明用的是哪个基类的成员
class Wood{ public: void show(){ cout<<"我是木头"<<endl; } }; class Desk{ public: void show(){ cout<<"我是桌子"<<endl; } }; class Woodest:public Wood,public Desk{ }; int main(){ Woodest wd; wd.Wood::show(); wd.Desk::show(); return 0; }
五.虚继承
虚继承存在的目的就是让很多个派生类继承它的时候,实际上用的是同一个基类,共享基类
举个例子
class A { public: int n; A(int a) :n(a) {}; }; class B :public A { public: int n; B(int a,int b) :A(a),n(b) {}; }; class C :public A { public: int n; C(int a, int b): A(a),n(b) {} }; class D:public B,public C { public: int n; D(int a,int b,int c,int d,int e):B(a,b),C(c,d),n(e) {} }; int main() { D d(1,2,3,4,5); while (1); return 0; }
这是没有用虚继承的,总共有五个n,各不相同,相当于B和C里面都构造了一个A
再看看下面
修改了一点代码,B,C继承A时为虚继承
class A { public: int n; A(){ cout << "调用无参构造器" << endl; }; A(int a) :n(a) { cout << "调用有参构造器" << endl; }; }; class B :virtual public A { public: int n; B(int a,int b) :A(a),n(b) {}; }; class C :virtual public A { public: int n; C(int a, int b): A(a),n(b) {} }; class D:public B,public C { public: int n; D(int a,int b,int c,int d,int e):B(a,b),C(c,d),n(e) {} }; int main() { D d(1,2,3,4,5); while (1); return 0; }
然后我们便发现
这里A中的n是没有初始化的,而且D中也多加了一个A中的n,并且这三个A中的n的值是一样的,不难发现它们三个是同一个n
再看看打印出来的东西,发现只调用了A的无参构造器一次
也就是说,使用虚继承之后,不会调用基类构造器来构造派生类中的基类成员,而且只会用无参构造器构造基类一次
还可以发现,在最终派生类调用的时候会优先调用A的构造函数一次,而且还是直接调用,不像普通类的调用是不能调用间接基类的构造函数的
六.类类型转换
类也是数据类型,也可以数据转换,我们可以通过数据转换访问类中的所有成员,包括private
1.将派生类对象赋值给基类对象
class A { int a; int b; public: A(int a, int b) :a(a), b(b){}; void show() { cout << "类A:\ta" << a << " b" << b << endl; } }; class B :public A { int c; public: B(int a, int b, int c) :A(a, b), c(c){}; void show() { cout << "类B:\tc" << c << endl; } };
可以看见并没有改变show成员函数,只改变了数值
实质上是成员变量的赋值
2.将派生类指针赋值给基类指针
可以看见,结果还是一样的,但是这个实际上是aa已经指向了bb,数据都是bb的,第一种情况是直接赋值,这个是指针指向,至于为什么show函数没有变,是因为编译器通过判断指针的类型来调用成员函数,这个类型还是类A的类型,就会去找类A的show函数
3.将派生类引用赋值给基类引用
同理,虽然aa只是bb的别名,但是aa的类型与bb不一样,调用的how方法也不一样