我们今天来学习C++的进阶:
面向对象三大特性:封装,继承,多态。
封装我们在前面已经学了,我们细细理解,我们的类的封装,迭代器的封装(vector的迭代器可以是他的原生指针,list迭代器不能是他的原生指针,我们进行封装),适配器的封装(stack和queue的封装)。
我们今天就来学习他的第二大特性:继承;
继承:
继承其实就是我们把公共的成员提取出来,方成一个单独的类,然后让下面的类去继承;
这个就是设计层次的类的复用。
我们来看我们的继承:
下⾯我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity⾝份认证的成员函数,设计到两个类⾥⾯就是冗余的。当然他们也有⼀些不同的成员变量和函数,⽐如⽼师独有成员变量是职称,学⽣的独有成员变量是学号;学⽣的独有成员函数是学习,⽼师的独有成员函数是授课。
我们看我们的上面的两个类,这两个类分别是代表的是学生和老师,但是我们发现这两个类的话,其中有一部分的函数和变量是一样的,他们有相同的成员变量和成员函数。
这样的话,设计是不是有一点冗余呢?是的。
这时候就是我们的继承的用法了:
我们看这个类,我们把这两个都有的类放到一起,构成一个新的类,这个新的类我们叫做父类。
我们看下面:
我们这里的继承是公有的继承,我们使用了public:来进行继承;
然后我们看我们现在的Student和Teacher类,这两个类现在里面有的数据就是他们自己也特有的数据,那他们以前的数据怎么办呢?
我们把他们之前的数据存起来的(父类)继承下来,继承给他们。这时候这两个类还是可以调用以前的函数和成员变量,他们都是还在的。
然后这些继承父类的类,我们叫他子类。
继承的方式:
如果我们想在父类里面和外面使用的话,我们就定义为public。
如果想在父类里面使用,外面不能使用的,但是继承的子类可以使用,我们就定义为protect。
如果父类里面使用,外面不能使用,继承的子类也不能使用的话,我们就定义为private。
当然如果是父类里面的private成员,无论怎样继承我们的子类都是不可见的,但是我们还有一种方法在子类中得到这个,我们可以在父类的public区域里面创建一个函数,
我们看这个代码,我们在我们的父类的public区域里面创造一个函数,父类的话,我们是可以使用我们的父类的private的成员变量的。在这个函数我们对我们的private的变量进行调整。
这个public的成员函数我们的继承的子类里面是可以调用的。
继承类模板:
我们的类模板也是可以被继承的:
我们之前实现我们的容器适配器Stack的时候,我们把我们的容器vector传过去,现在的话,我们把我们的vector继承给我们的Stack;
但是这个方式是没有我们之前学习Stack的时候,实现的,我们使用的组合的方式实现效果好的。
基类和派生类之间的转换:
我们看这个图片:Person表示我们的基类,Student表示我们的派生类。
临时对象:
我们看这个,我们之前学习的,我们看第一个图片,我们的i赋值给d,int类型的赋值给double类型的,里面存在着隐式类型转换,如果有隐式类型转换的话,他就会产生临时对象。
这时候我们看第二个图片,如果我们加上引用的符号的话,这时候就会报错,因为我们的临时对象具有常性,所以我们要引用的话就要加上const。这才是对的。
我们再看我们的自定义类型的:
我们的这个string,我们说单参数的构造函数支持隐式类型转换,这里"1111"
是const char*
类型的字符串字面量,这是一个常量字符串,编译器会隐式调用std::string
对应的单参数构造函数,将const char*
类型的"1111"
转换为std::string
类型,并初始化s1
。
我们的第二行的这个代码,我们引用了一个临时对象,还是临时对象具有常性。使用const防止被修改。
在 C++ 中,单参数的构造函数支持隐式类型转换,是指当一个构造函数只有一个参数时,编译器可以自动使用该构造函数将参数类型转换为类类型。
在上述代码中,MyClass
类有一个接受int
类型参数的单参数构造函数。在main
函数中,使用int
类型的10
初始化MyClass
类型的对象obj
,此时编译器会自动调用单参数构造函数,将10
隐式转换为MyClass
类型的对象。
这个都会产生临时对象;
但是我们这里的没有产生临时对象;这里我们的是特殊处理;他叫复制兼容转换;
我们继续看:
我们的派生类对象可以赋值给我们的基类的对象(这个我们后面讲解),但是我们的基类的对象不能赋值给我们的派生类。
继承中的作用域:
我们说我们的基类和派生类都有他自己的作用域。
这两个不同的作用域,我们可以有相同名字的变量。
我们看这个图片,当我们的基类和我们的派生类里面都有我们的相同名字的成员变量的时候,我们在我们的派生类里面,我们访问同名的成员变量的时候,我们访问的是派生类里面的。
派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。(会把基类里面的成员变量屏蔽掉)(如果在派生类里面想要访问基类里面的成员的话,我们就要指定我们的类域);
需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。(只要是函数的名字一样,就构成隐藏,返回值和函数参数全都不看)。(在派生类里面把基类的函数隐藏了,如果派生类的对象想要使用基类里面的函数,我们就要指定作用域)