继承的概念及使用
一. 继承的概念及定义
1.1 继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。
举例说明:下面我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。当然他们也有一些不同的成员变量和函数,比如老师独有成员变量是职称,学生的独有成员变量是学号;学生的独有成员函数是学习,老师的独有成员函数是授课。
class Student
{
public:void identity(){// ...}void study(){// ...}
protected:string _name = "peter"; // 姓名string _address; // 地址string _tel; // 电话int _age = 18; // 年龄int _stuid; // 学号
};
class Teacher
{
public:void identity(){// ...}void teaching(){//...}
protected:string _name = "张三"; // 姓名int _age = 18; // 年龄string _address; // 地址string _tel; // 电话string _title; // 职称
};
int main()
{return 0;
}
那么我们要如何去解决代码冗余的问题呢?这个时候就要使用继承来解决问题,那么继承又是如何使用呢?现在先简单的说一下继承的格式,一般我们是有父类和子类,子类去继承父类中的成员变量,实现代码的复用,那我们就拿上面这个类来举例,把公共的成员变量放在父类中,然后用子类去继承父类:
class person
{
public:void indentify(){//...}
protected:string _name="name";int _age = 20;string _address;string _tel;
};class student : public person
{
public:void study(){//...}
protected:int _stuid; //学号
};class teacher :public person
{
public:void teachering(){//...}
protected:int _title; //职称};
int main()
{student s;teacher t;s.indentify();t.indentify();return 0;
}
1.2 继承定义

下面我们来简单的讲一下继承基类成员访问的方式有哪些?

通过上面的表格我们判断基类private成员在派生类(子类)中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。其次就是如果只想让派生类访问而不想在类外被访问,就可以使用protected,最后我们要强调一下在实际的开发中一般使用都是public继承,几乎很少使用protected和private继承,因为这样的话各个类中的成员只能在类中使用,实际扩展维护性不强。
(使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。)
1.3 继承类模板
继承类模板又是如何使用的呢?下面就用stack继承vector来举例子吧,具体的注意事项我会写在代码中:
#include <vector>
using namespace std;
namespace zda
{template<class T>class stack :public vector<T>{public:void push(const T& x){//基类是类模板的时候,我们要指定一下类域,因为stack<int>实例化的时候也//实例化了vector<int>了vector<T>::push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};
}int main()
{zda::stack<int> s1;s1.push(1);s1.push(1);s1.push(1);return 0;
}
二. 继承的使用
2.1 基类和派生类之间的转换
1. public继承的派生类对象,可以赋值给基类的指针或者是引用。这里有个形象的说法叫切片或者切割。寓意把派生类中基类那部分切出来,基类指针或引用指向的是派生类中切出来的基类那部分。
2.基类对象不能赋值给派生类对象。
3. 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。

同样的如果代码中出现s1=p1的行为这里就会编译报错,因为基类对象不能赋值给派生类对象。
2.2 继承中的作用域
在基本了解了继承的体系之后,我们也要知道虽然派生类可以继承父类中的允许被访问的成员变量,但是两者仍然有独立的作用域。第一,派生类和基类中有同名成员,派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派生类成员函数中,可以使用基类::基类成员显示访问) 。第二,需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。 注意在实际中在继承体系里面最好不要定义同名的成员。
