创建博客网站,百度seo教程,莱芜区平台公司,南京网络营销公司#x1f493;博主CSDN主页:杭电码农-NEO#x1f493; ⏩专栏分类:C从入门到精通⏪ #x1f69a;代码仓库:NEO的学习日记#x1f69a; #x1f339;关注我#x1faf5;带你学习C #x1f51d;#x1f51d; 继承 1. 前言2. 继承的基本概念3. 继承关系和访… 博主CSDN主页:杭电码农-NEO ⏩专栏分类:C从入门到精通⏪ 代码仓库:NEO的学习日记 关注我带你学习C 继承 1. 前言2. 继承的基本概念3. 继承关系和访问限定符4. 继承中的作用域5. 父子类的对象赋值转换6. 子类中的默认成员函数7. 继承与友元,继承与静态变量8. 菱形继承和虚拟继承9. 总结以及拓展 1. 前言
接下来的几篇博客会进入C 继承和多态的学习,在校招笔试 和面试中这一章节考察的很多! 请同学们耐心学习!
本章重点: 本篇文章着重讲解继承的概念和定义, 父类和子类的对象赋值转换, 继承中的作用域以及子类的默认成员函数 以及继承和友元,继承和静态成员的关系 最后讲解菱形继承和虚继承概念 2. 继承的基本概念
继承,其实就是一种代码的复用手段 子类继承父类就能用父类中的变量!
举一个例子: 在师生管理系统中,有学生和老师两个 角色,学生和老师的共同信息有:姓名 性别,年龄和身高等等,然而学生又有一些 专有的信息,比如学号和所属学院 老师也有专属信息如:工号和所教学科 这样就可以将师生的共同信息提取出来:
struct Person
{string name;string sex;int age;int height;
}在实现student类和teacher类时 只需要继承上面的person类即可!
class Student : public Person
{
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};派生类也被称为子类 基类也被称为父类
父子类成员的使用:
Student st;
st._stuid123456;
st.name张三;
st.age20;子类对象中课直接调用父类变量! 3. 继承关系和访问限定符
继承方式和访问限定符一样有三种:
继承的方式不同,那么子类中继承 到的父类的变量的访问权限就不同 可以用下面的表格来表示它们的关系: 我简单的总结以下几点:
无继承体系中,protected和private没有区别在继承体系中,父类的protected成员在子类 中也是protected或保护成员父类的private成员在子类是不可见的! (继承下来了但不能使用)实际中使用继承时一般都用public继承使用关键字class时默认的继承方式是private 使用struct时默认的继承方式是public 4. 继承中的作用域
先说以下结论:
继承体系中基类和子类有独立的作用域子类和父类中有同名成员时,子类成员将屏蔽父类对同名成员的直接访问这种情况叫隐藏也叫重定义在子类成员函数中,可以使用基类::基类成员显示访问需要注意的是如果是成员函数的隐藏 只需要函数名相同就构成隐藏实际中在继承体系里面最好不要定义同名的成员
class Person
{
protected :int _num 111; // 身份证号
};class Student : public Person
{
protected:int _num 999; // 学号
};在main函数中定义student对象 后再打印_num默认为子类中的_num 若想打印父类中的_num,需要指定类域
Student st;
coutst._num;
coutst.Person::_num;有同学可能会疑惑:函数名相同的话 不应该是构成函数重载吗?是的,在同一 作用域下,函数名相同确实构成函数重载 但是父子类是不同作用域,这里是构成隐藏! 5. 父子类的对象赋值转换
老样子,先说结论: 子类对象可以赋值给基类的 -/对象/基类的指针/基类的引用 基类对象不能赋值给派生类对象 注意这里能够赋值不是隐式类型转换! 6. 子类中的默认成员函数
还记得类的六个默认成员函数吗? 就是不显示写系统会自动生成的: 子类的默认成员函数有哪些特殊的行为? 下面我直接给出结论:
子类的构造函数必须显示调用父类的构造 去初始化父类的那部分成员(拷贝构造也是)子类的operator中必须调用父类的 operator完成父类成员赋值子类的析构函数不用显示调用父类的析构 编译器会自动去调用子类初始化对象时,先初始化父类的成员变量 再初始化子类的成员变量子类析构清理时先调用子类的析构函数 再调用父类的析构函数(与构造反过来)
可以使用以下代码区验证此结论:
class Person
{
public :Person(const char* name peter): _name(name ){coutPerson() endl;}Person(const Person p): _name(p._name){coutPerson(const Person p) endl;}Person operator(const Person p ){coutPerson operator(const Person p) endl;if (this ! p)_name p ._name;return *this ;}~Person(){cout~Person() endl;}
protected :string _name ; // 姓名
};
class Student : public Person
{
public :Student(const char* name, int num): Person(name ), _num(num ){coutStudent() endl;}Student(const Student s): Person(s), _num(s ._num){coutStudent(const Student s) endl ;}Student operator (const Student s ){coutStudent operator (const Student s) endl;if (this ! s){Person::operator (s);_num s ._num;}return *this ;} ~Student(){cout~Student() endl;}
protected :int _num ; //学号
};
void Test ()
{Student s1 (jack, 18);Student s2 (s1);Student s3 (rose, 17);s1 s3 ;
}7. 继承与友元,继承与静态变量
继承与友元的关系很简单一句话: 友元关系不能继承 也就是说基类友元不能访问子类私有和保护成员 继承和静态成员的关系也很简单: 基类中定义的静态成员被整个继承体系共享 整个继承体系里面只有一个这样的成员 无论派生出多少个子类 都只有一个static成员实例 8. 菱形继承和虚拟继承
在使用继承时会遇见以下情况:
类B继承了类A,类C也继承了类A 然而类D继承了类B和C 此时会有一个问题,类D的实例化对象中 有类B和类C,然而B类和C类都有A类 所以说D类对象中的A类成员就重复了!
class A
{int _a 1;
};
class B :public A
{int _b 2;
};
class C :public A
{int _c 3;
};
class D :public B, A
{int _d 4;
};我们通过内存窗口观察一下: D对象中有两个_a,一个在B类一个在C类 这就造成了数据冗余,于是可以使用虚拟继承 来解决这一问题:
虚拟继承:在继承前加上virtual关键字
class A
{int _a 1;
};
class B :virtual public A
{int _b 2;
};
class C :virtual public A
{int _c 3;
};
class D :public B, A
{int _d 4;
};注意,只用腰部的类加上virtual即可! virtual这一关键字在多态还有大用处! 9. 总结以及拓展
继承是多态的基础,而笔试面试的时候 继承和多态是考察的很多的,希望同学们 把基础打扎实.当然关于继承的内容其实不止 这些,这些只是最重要的内容,关于继承问题 我们将在下一章节:多态时再展开叙述
对于is-a和has-a的拓展阅读:
拓展阅读 下期预告:C继多态