继承

1.继承的概念及定义

1.1继承的概念定义:
继承机制是面对对象程序设计使代码可以复用的重要手段,他允许程序员保持原有类特星的基础上进行扩展,增加功能,这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,以前我们接触的复用都是函数的调用,继承是类设计层次的复用。

class Person
{
public:
    void Print()
    {
    cout<<"name:"<<_name<<endl;
    cout<<"age"<<_age<<endl;
    }
protected:
    string _name="peter";
    int _age=18;        
};
class Student:public Person
{
  protected:
  int _stuid;  
};
class Teacher :public Person
{
protected:
    int _jobid;
};

继承后父类的成员(成员函数+成员变量)都会变成子类中的一部分,这里体现了Student和Teacher复用了Person的成员。

1.2继承的定义

1.2.1继承定义的格式
派生类 继承方式 基类
class Student :public Prerson
{}
1.2.2继承方式和访问限定符

在这里插入图片描述
在这里插入图片描述

1.2.3继承基类成员访问方式
基类成员在子类中的访问权限:min(在基类中的限定符,继承方式)
在这里插入图片描述

protected:在类外无法访问,但在子类内部可以访问。
private:在类外和子类都无法访问(不可见)
public继承:不改变基类在子类中的访问权限
protected继承:基类成员在子类中的最低访问权限为protected
private继承:基类成员在子类中的最低访问权限为private

class默认继承方式为private继承
struct默认继承方式为public继承

2.基类和子类之间的赋值转换

切片操作(大的能赋给小的,小的不能赋给大的):
1.子类对象可以赋值给父类对象,引用,指针
2.父类对象不能赋值给子类对象,可能会造成访问异常
3.父类指针在特定情况下强制类型转换赋值给子类对象指针,但是必须是父类指针是子类对象的指针才是安全的

int a=10;
float b=1.5;

隐式类型转换:类型相似的变量可以进行相互赋值,通过隐式类型转换

a=b;

强制类型转换

int *pa=a;
int c=10;
pa=(int *)c;

切片:子类对象何以赋值类父类对象,父类对象不能辅助类子类对象,子类可以赋给父类引用,引用底层就是指针,父类的指针不能赋给子类,可能会发生访问越界

Person *Ptr=&Stu;
Person& ret=Stu;

**

3.继承中的作用域

**

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定
    义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。
    成员隐藏
    1.成员变量隐藏:子类相同名称的成员,隐藏基类同名称的成员,通过加基类作用域可以访问基类成员变量
    2.成员函数的隐藏:基类和子类函数名相同就构成隐藏,和函数参数无关
    重载:函数在同一作用域,函数名相同,参数不同
    重定义/隐藏:子类函数和父类函数名相同

4.派生类的默认成员函数

构造:
1.默认生成的,首先会自动自动调用父类的默认构造函数,再调用自己的构造函数。
2.显示定义:如果父类有默认构造,可以不显示调用,如果父类无默认构造,需要在初始化列表显示调用。
拷贝构造:
1.默认生成的,编译器会自动调用父类的拷贝构造。
2.显示定义:如果不显示调用父类的拷贝构造,编译器会调用父类的拷贝构造
赋值运算符:
1.默认生成的,编译器会自动调用父类的赋值赋值运算符重载函数
2.显示定义:如果不显示调用父类的赋值运算符重载函数,编译器不会调用父类的任何函数
析构:
1.默认生成,
2.不需要显示调用父类析构,编译器会自动调用,注意父类析构和子类析构构成函数隐藏
取地址:
使用编译器默认生成的即可

构造父类的成员变量不能直接在子类的构造函数和拷贝构造直接初始化,必须调用父类的构造和拷贝构造进行初始化

子类构造先调用父类的构造函数,再调子类的构造函数
如果子类不显示调用父类的拷贝构造,则会调用父类的默认构造函数,
如果写了调用父类的拷贝构造则调用父类的拷贝构造

父类的成员变量可以直接在子类的运算符重载中进行赋值
如果是编译器默认生成的子类赋值运算符重载函数,会调用父类的运算符重载函数进行赋值运算符的重载
建议直接调用父类的赋值运算符重载函数,函数复用

**析构函数:**不允许显示调用父类的析构函数
构造和析构的调用顺序(函数栈帧):
构造顺序:基类构造,派生类构造
析构顺序:派生类析构,基类析构

5.友原关系不能被继承

6.继承和静态成员:

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

7.多继承:一个子类有多个直接父类

单继承:一个子类只有一个直接父类
菱形继承:两个子类继承同一个父类,而又有一个子类同时继承这两个类
存在问题:数据冗余和二义性
数据冗余:数据重复的现象,同一份数据储存在不同的数据文件中。
二义性:如果文法G存在不只一个语法树,则称该语句是二义性的。在菱形继承的时候子类同时继承两个父类,且两个父类存在同名变量,当子类在调用父类的同名变量时,将出现二义性,因为编译器不知道你需要访问的哪个父类中的变量
解决办法:
虚拟继承:
通过虚基表解决数据冗余和二义性
当前部分相当于公共部分的偏移量
公共部分在对象模型的最下面:直接父类部分通过各自的虚基表中的偏移量访问公共部分
虚基表:当前对象相对基类部分的偏移量
切片操作需要通过偏移量获取自己直接基类成员
继承和组合:
继承:白箱复用,耦合度高,突破封装。public继承:is a(每一个派生类对象都是一个基类对象)
组合:黑箱复用,耦合度底,has a(B组合A,每一个B对象中都有一个A对象)
优先使用组合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值