代码网站开发,网站开发员招聘,湖南益阳新增本土确诊0例,免费推广企业网站一.继承基础知识
继承定义#xff1a;
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段#xff0c;它允许程序员在保 持原有类特性的基础上进行扩展#xff0c;增加功能#xff0c;这样产生新的类#xff0c;称派生类。继承呈现了面向对象 程序设…一.继承基础知识
继承定义
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许程序员在保 持原有类特性的基础上进行扩展增加功能这样产生新的类称派生类。继承呈现了面向对象 程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用继 承是类设计层次的复用
例如:
class Person
{
public://
private:int _age;string cellphone;string name;
};
class Student :public Person
{
public://
private:int _student_id;
};
class Teacher :public Person
{
public://
private:int _teacher_id;
};
你仔细观察上面代码会发现再学生和老师类上我们后面都跟了:public Person说明这两个类是继承Person的内容 现在我们再来学习下格式
定义格式 关于派生类和基类大家看下都能理解现在我们重点来讲解下什么是继承方式
继承方式简单理解就是子类对于父类中的成员可以进行的操作如下
类成员/继承方式public继承protected继承private继承基类的public成员派生类的public成员派生类的protected 成员派生类的private 成员基类的protected 成员派生类的protected 成员派生类的protected 成员派生类的private 成员基类的private成 员在派生类中不可见在派生类中不可见在派生类中不可 见
总结 1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。 2. 基类private成员在派生类中是不能被访问如果基类成员不想在类外直接被访问但需要在 派生类中能访问就定义为protected。可以看出保护成员限定符是因继承才出现的。 3. 实际上面的表格我们进行一下总结会发现基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 Min(成员在基类的访问限定符继承方式)public protected private。 4. 使用关键字class时默认的继承方式是private使用struct时默认的继承方式是public不过 最好显示的写出继承方式。 5. 在实际运用中一般使用都是public继承几乎
很少使用protetced/private继承也不提倡 使用protetced/private继承因为protetced/private继承下来的成员都只能在派生类的类里 面使用实际中扩展维护性不强
二.基类和派生类对象赋值转换
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象。 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类 的指针是指向派生类对象时才是安全的。这里基类如果是多态类型可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换
如下
class Person
{
public:
private:int age;string name;
};
class Student :public Person
{
public:
private:string _id;
};
class Teacher :public Person
{
public:
private:string _id;
};
int main()
{Student s;//子类可以切片给父类对象//派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用//派生类对象 可以赋值给 基类的对象Person p1 s;//派生类对象 可以赋值给 基类的指针Person* p2 s;//派生类对象 可以赋值给 基类的引用Person p3 s;//但注意;基类不能赋值给派生类Person p4;//Teacher t1 p4;return 0;
}
但基类一定不能传给派生类吗 我们发现此时是可以赋值给子类了这是特殊情况的处理而且转换时可能会出现越界的情况 知识扩展
我们知道在不同类型赋值时会存在类型转换那么你知道下面这行代码出错的原因吗 看到报错原因你以为是int不能传给double还是要强制类型转换 实际上这是因为引用赋值时会产生一个临时变量该零时变量具备常性所以我们必须用const修饰防止权限扩大 这样就可以通过了
总结如果要通过引用来复制拷贝我们一定要观察是否会出现零时变量从而确定是否需要用const修饰 三.继承中的作用域
1. 在继承体系中基类和派生类都有独立的作用域。 2. 子类和父类中有同名成员子类成员将屏蔽父类对同名成员的直接访问这种情况叫隐藏 也叫重定义。在子类成员函数中可以使用 基类::基类成员 显示访问 3. 需要注意的是如果是成员函数的隐藏只需要函数名相同就构成隐藏。 4. 注意在实际中在继承体系里面最好不要定义同名的成员
如果你仔细观察过上一个代码你会发现在Student和Teacher中都出现了_id这个成员变量这就是隐藏
下面在看这道题
class A
{
public:void func(){//}
private:int _a;
};
class B :public A
{
public:void func(int a10){//}
private:int _a;
};
请问B中的func函数和A中的func函数是什么关系
a. 无关系 b. 重定义隐藏 c.重载
答案是:b
c重载是指在相同作用域中两个同名函数由于参数的不同所构成的而且只有返回类型不同是不构成重载的
而且这两个类属于不同作用域看定义:成员函数的隐藏只需要函数名相同就构成隐藏
那么我们如何访问父类成员变量呢
如下 四.派生类的默认成员函数(重点
class Person
{
public://构造函数;Person(const string s):_name(s){}//拷贝构造Person(const Person p):_name(p._name){}//复值重载Person operator(const Person p){if (p ! this){this-_name p._name;return *this;}else{exit(-1);}}//析构函数~Person(){//析构}
protected:string _name;
};
class Student :public Person
{
public://构造函数;//派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认//的构造函数则必须在派生类构造函数的初始化列表阶段显示调用Student(const Student s):_age(s._age),Person(s){}Student(const int age,const string name):_age(age),Person(name){}//注意的是:构造函数实际给成员变量赋值时是先给父类赋值的即使我们初始化列表中子类在前//拷贝构造//派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化Student(const Student s):_age(s._age),Person(s)//注意这里是切割{}//赋值重载//派生类的operator 必须要调用基类的operator 完成基类的复制Student operator(const Student s){if(this!s){_age s._age;Person::operator(s);//注意这里operator函数和父类是隐藏的关系}else{exit(-1);}}//析构函数://派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能//保证派生类对象先清理派生类成员再清理基类成员的顺序//析构先析构子类构造先构造父类~Student(){//析构子类//父类会自动调用的不用写}
protected:int _age;
}; 五.友元和静态成员
友元关系不能继承也就是说基类友元不能访问子类私有和保护成员
此时我们写成友元就可以访问了
基类定义了static静态成员则整个继承体系里面只有一个这样的成员。无论派生出多少个子 类都只有一个static成员实例 即我们可以通过父类和子类访问同一个静态成员 六.复杂的菱形继承及菱形虚拟继承
单继承一个子类只有一个直接父类时称这个继承关系为单继承 多继承一个子类有两个或以上直接父类时称这个继承关系为多继承 此时就会出现一种特殊的继承;菱形继承菱形继承是多继承的一种特殊情况 菱形继承的问题可以看出菱形继承有数据冗余和二义性的问题。在d的对象中A成员会有两份
此时我们通过限定符去访问A中的成员对象就会出现B和C都可以访问而且访问的是不同的两个成员此时就会出现二义性和冗杂
此时C引入了虚继承的概念。
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系在B和C的继承A时使用虚拟继承即可解决问题。需要注意的是虚拟继承不要在其他地方去使用。
class Person
{
public:
protected:string _name; // 姓名
};
class Student : virtual public Person
{
protected:int _num; //学号
};
class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
如图虚继承只需要在后面写上一个virtual即可还要注意的是不同的父类要通过分隔开
接下来我们深入底层来看看虚继承是如何解决问题的。 菱形继承例子 这就是一个典型的菱形继承关系 最后感谢大家的支持