文章目录前言一、继承的概念二、继承的定义1.定义格式2. 继承基类成员访问方式的变化3.继承类模板三、基类和派生类间的转换四、 继承中的作用域1.隐藏规则2.具体实践总结前言在写代码时我们经常面临一个问题两个类具有相同成员变量/函数这会导致代码的冗余比如Student和Teacher都有姓名/地址/电话/年龄等成员变量都有identity身份认证的成员函数。当然他们也有一些不同的成员变量和函数比如老师独有成员变量是职称学生的独有成员变量是学号学生的独有成员函数是学习老师的独有成员函数是授课。在此背景下C设计出了继承。一、继承的概念继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许我们在保持原有类特性的基础上进行扩展增加方法(成员函数)和属性(成员变量)这样产生新的类称派生类。继承呈现了面向对象程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用继承是类设计层次的复用。实例// 父类Person 人classPerson{public:// 身份认证进校园、图书馆、实验室扫码voididentity(){coutvoid identity() _nameendl;}protected:string _name张三;int_stuid;// 学号/人员编号string _address;// 地址string _tel;// 电话int_age18;// 年龄};// Teacher 公有继承 PersonclassTeacher:publicPerson{public:// 授课voidteaching(){cout_name 正在授课endl;}protected:string title;// 职称};// Student 公有继承 Person补全classStudent:publicPerson{public:// 学习voidstudy(){cout_name 正在学习endl;}protected:// 学生独有的成员例如专业string major;};二、继承的定义1.定义格式下面我们看到Person是基类也称作父类。Student是派生类也称作子类。(因为翻译的原因所以既叫基类/派生类也叫父类/子类)2. 继承基类成员访问方式的变化基类 private 成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。基类 private 成员在派生类中是不能被访问如果基类成员不想在类外直接被访问但需要在派生类中能访问就定义为 protected。可以看出保护成员限定符是因继承才出现的。实际上面的表格我们进行一下总结会发现基类的私有成员在派生类都是不可见。基类的其他成员在派生类的访问方式Min (成员在基类的访问限定符继承方式)public protected private。使用关键字 class 时默认的继承方式是 private使用 struct 时默认的继承方式是 public不过最好显示的写出继承方式。// class 默认 private 继承classBase{};classSon:Base{};// 等价于 class Son : private Base{}// struct 默认 public 继承structBase2{};structSon2:Base2{};// 等价于 struct Son2 : public Base2{}在实际运用中一般使用都是 public 继承几乎很少使用 protetced/private 继承也不提倡使用 protetced/private 继承因为 protetced/private 继承下来的成员都只能在派生类的类里面使用实际中扩展维护性不强。注意实际日常中最常用这两个同时想要快速记住是否可用只要看谁的权限小就能用谁。3.继承类模板namespaceMY{//templateclass T//class vector//{};// stack和vector的关系既符合is - a 也符合has - atemplateclassTclassstack:publicstd::vectorT{public:voidpush(constTx){//基类是类模板时需要指定⼀下类域//否则编译报错//: error C3861 : “push_back”://找不到标识符//因为//stackint//实例化时也实例化vectorint了//但是模版是按需实例化//push_back等成员函数未实例化所以找不到。vectorT::push_back(x);//push_back(x);}voidpop(){vectorT::pop_back();}constTtop(){returnvectorT::back();}boolempty(){returnvectorT::empty();}};}记住普通类 继承 类模板必须指定模板参数三、基类和派生类间的转换通常情况下我们把一个类型的对象赋值给另一个类型的指针或者引用时存在类型转换中间会产生临时对象所以需要加 const如int a 1; const double d a;public 继承中就是一个特殊处理的例外派生类对象可以赋值给基类的指针 / 基类的引用而不需要加 const这里的指针和引用绑定是派生类对象中的基类部分如下图所示。也就意味着一个基类的指针或者引用可能指向基类对象也可能指向派生类对象。派生类对象赋值给基类对象是通过基类的拷贝构造函数或者赋值重载函数完成的 这个过程就像派生类自己定义部分成员切掉了一样所以也被叫做切割或者切片如下图中所示。基类对象不能赋值给派生类对象。因为派生类对象具有基类对象没有的成员变量或函数。基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。classPerson{protected:string _name;// 姓名string _sex;// 性别int_age;// 年龄};classStudent:publicPerson{public:int_No;// 学号};intmain(){Student sobj;// 1.派生类对象可以赋值给基类的指针/引用Person*ppsobj;Personrpsobj;// 派生类对象可以赋值给基类的对象是通过调用后面会讲解的基类的拷贝构造完成的Person pobjsobj;//2.基类对象不能赋值给派生类对象这里会编译报错// sobj pobj;return0;}四、 继承中的作用域1.隐藏规则在继承体系中基类和派生类都有独立的作用域。查找的规则为遵循由内到外的名字查找顺序先在派生类自身的作用域中搜索这个名字如果找到匹配的名字查找立即终止不会再往上层搜索如果派生类中没找到才会进入基类作用域继续查找。派生类和基类中有同名成员派生类成员将屏蔽基类对同名成员的直接访问这种情况叫隐藏。在派生类成员函数中可以使用基类基类成员显式访问需要注意的是如果是成员函数的隐藏只需要函数名相同就构成隐藏。与参数列表、返回值类型是否一致完全无关。也就是说哪怕派生类的函数和基类函数参数不同、返回值不同只要名字一样派生类的函数就会把基类中所有同名函数包括全部重载版本全部隐藏。注意在实际中在继承体系里面最好不要定义同名的成员。2.具体实践下面我们来看一个例子来具体分析一下。classA{public:voidfun(){coutfunc()endl;}};classB:publicA{public:voidfun(inti){coutfunc(int i)iendl;}};intmain(){B b;b.fun(10);b.fun();return0;};A和B类中的两个func构成什么关系按照我们的规则它们在不同作用域且函数名相同所以那它就是隐藏。上面程序的编译运行结果是什么这题的答案是编译报错。执行 b.fun()编译器先在 B 类派生类的作用域找名字fun找到了 B::fun(int i)只要找到同名查找就停止不会再去基类 A 里面找现在找到的函数要求传入一个 int但是代码里 b.fun() 没有传参数参数列表不匹配。直接报编译错误不是运行时错误。注意很多人以为会自动去找 A 的无参 fun这是错的。派生类的同名函数会屏蔽基类所有同名函数不管参数一不一样。总结由于继承知识较多我们分为上下两个篇章来分析有兴趣请关注我的主页。