欢迎光临
我们一直在努力

C++进阶 继承

一.继承

1.继承的概念

C++ 中的继承,就是让一个类“继承”另一个类已有的成员,实现代码复用和功能扩展。

基本形式:

 

这里:

父类 也叫 基类

子类 也叫 派生类

子类 : public 父类 表示子类以 public 方式继承父类。

C++ 继承的核心作用是:子类可以复用父类已有代码,并在此基础上增加自己的新功能。

 此时 Student 对象不仅有自己的 stuId,还拥有从 Person 继承来的 name、age 和 Print()。

可以理解为:

学生是一种人,所以 Student 可以继承 Person。

2.定义格式

继承方式主要有三种:

最常用的是 public 继承。 

public 继承:父类成员在子类中基本保持原来的访问权限。

protected 继承:父类 public 和 protected 成员到了子类中都变成 protected。

private 继承:父类 public 和 protected 成员到了子类中都变成 private。

注意:父类的 private 成员也会被继承到子类对象中,但子类不能直接访问。

 3.继承类模版

C++ 中“继承类模板”,就是:一个类模板可以继承另一个类模板实例化后的类。

基本格式:

 这里:

不是继承 Base,而是继承 Base<T>。

因为 Base 本身只是一个模板,还不是具体类型;Base<int>、Base<double> 才是具体类型。

也就是说,Derived<int> 继承的是 Base<int>。固定类型继承:

这里不管 Derived<T> 的 T 是什么,它都继承 Base<int>。

 实际是:

 所以父类里的 _data 类型永远是 int。

普通类继承类模版

这是可以的,因为 Base<int> 已经是一个具体类。

但这样不可以:

 错误原因:Base 只是模板,不是类型。

总结:

类模板不能直接被继承,必须先实例化成具体类型,比如 Base<T> 或 Base<int>,才能作为父类。

二.父类和子类之间的转换

 

 1. 子类转父类(向上转型)

对象转换

 执行后:

id 被丢弃了。

这叫对象切片 。

指针转换

 合法。

因为学生一定是人。

所以:

 可以转换为:

 引用转换

 此时:

 实际上修改的是:

 2. 父类转子类(向下转型)

一般不允许。

 错误

因为人不一定是学生。

指针也不允许

 错误

因为Person对象中根本没有id成员。

总结:

子类拥有父类的全部内容,所以子类可以当父类用。
父类不拥有子类新增的内容,所以父类不能随便当子类用。

三.继承中的作用域

1. 作用域的概念

 a属于类A的作用域,b属于类B的作用域

2. 继承中的作用域

当派生类继承基类后:

 Student对象中既有

 因此:

都可以访问

3. 同名成员隐藏

如果派生类定义了与基类同名的成员

 此时:

 访问的是:

 而不是:

 这称为隐藏派生类成员会隐藏基类同名成员

4. 如何访问基类成员

 输出: 学生
          父类

5. 函数也会发生隐藏

很多人误以为函数重载会生效。

实际上在继承中:

只要名字相同,就会隐藏。

此时:

正确

但:

 错误

因为:

已经被隐藏了

编译器只会在Student作用域中查找:

找不到无参版本。

6. 调用基类同名函数

 输出: Person

四.派⽣类的默认成员函数

1.派生类的构造函数

 创建对象:

 执行对象:

 先构造父类,再构造子类。

如果不调用

 编译失败。

因为Person没有默认构造函数,编译器不知道如何构造基类部分。

2. 派生类的拷贝构造

基类部分先拷贝

 输出:Person拷贝构造
         Student拷贝构造

若不写:

 编译器自动生成:

本质上:

调用基类拷贝构造 + 拷贝自己的成员。

3. 派生类赋值运算符重载

 顺序:

 为什么要写 Person::operator=()

因为_name属于基类。

派生类不能直接完成基类部分的赋值工作。

必须调用:

4. 派生类析构函数

 生命周期结束:

为什么顺序相反?

构造:

 析构:

类似:

盖房子先打地基再盖二楼,拆房子先拆二楼再拆地基

五.继承与友元

1.友元关系不能继承

Display()是Person的的友元,但不是Student的友元。

可以访问_name

会报错

因为Display不是 Student 的友元

无法访问:_id

2. 怎样访问派生类私有成员

必须再次声明友元

 

 才合法

3. 基类友元可以访问继承来的基类成员

这是合法的 

因为Student可以向上转换为Person&,而Show访问的是Person::_name,不是Student::_id

六.继承与静态成员

在继承中,静态成员是整个类共享的,不属于某个具体对象。因此继承后,基类和派生类访问的是同一份静态成员。

访问 :

或者:

都可以 

因为:

 实际上是同一个变量

七. 多继承及其菱形继承问题

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承

多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

 

1.多继承

 其中Derive同时拥有Base1中的成员,Base2中的成员,自己的成员。

例子:

 输出: 张三
          学习

2.菱形继承

这是多继承最大的坑

 出现两个: _name

访问:

 编译错误

因为:

都有

编译器不知道该选哪个

菱形继承不仅有二义性,还有数据重复

同一个人名存了两份。

浪费空间。

3.虚继承解决菱形继承

C++提供virtual关键字。

 结构就变成:

 此时Person只有一份。

访问:

 合法

因为_name只有一个。

八.继承和组合

1.什么是继承

继承表示:

is-a(是一个)关系

这里Student 是一个 Person

即Student is a Person

2.什么是组合

组合表示:

has-a(拥有)关系

这里Car 拥有 Engine

即Car has a Engine

3. 如何判断用继承还是组合

看关系

1.是一个(is-a)

学生 是一个 人

猫 是一个 动物

圆 是一个 图形

适合继承

2.拥有(has-a)

汽车 有 发动机

电脑 有 CPU

学校 有 学生

适合组合

4. 组合优于继承

 后来增加:

 如果用继承:

 想换发动机,car必须重写

如果用组合:

 直接换

 扩展性更强。

 

赞(0)
未经允许不得转载:171主机测评 » C++进阶 继承
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址