C++入门11——详解C++继承(菱形继承与虚拟继承)-1

简介: C++入门11——详解C++继承(菱形继承与虚拟继承)-1

1.继承的概念与定义

在学校的作业中,我们大部分人似乎都遇到过同样的一个作业:设计学校人员管理系统。在这个作业中我们要分别设计不同身份的人员信息,

比如老师:

学生:

学校里不仅有教师、学生,还有职工、学校不同部门的领导、外聘人员......

这么多不同身份的人员,我都要设计这么多的人员类吗?可能一个学校的人员还不算很复杂,但是以后如果遇到给不同的图书设计信息管理系统,成百上千种不同类型的图书,难道我就要设计成百上千种图书类吗?针对类似的问题,C++给出了继承的概念:

1.1继承的概念:

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

比如上述问题,我们就可以为各个人员类设计出一个父类,让成员类复用父类:

(父类包含各个子类的公有属性,每个子类再单独列出自己的独有属性)

#include <iostream>
 
using namespace std;
 
class Person
{
public:
  void Print()
  {
    cout << "name:" << _name << endl;
    cout << "age" << _age << endl;
  }
protected:
  string _name = "张三";//姓名
  int _age = 18;//年龄
};
 
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。
 
class Student :public Person
{
protected:
  int _stuid;//学号
};
 
class Teacher :public Person
{
protected:
  int _jobid;//工号
};
 
int main()
{
  Student s;
  Teacher t;
  s.Print();
  t.Print();
  return 0;
}

我们打开监视窗口看一下s和t对象:

我们的Student和Teacher类中并没有定义_name和_age变量,可是在监视窗口却发现了这两个变量,这就说明子类复用了父类的变量。

结果运行如图:

Student和Teacher类中并没有Print函数,而结果却显示调用了Print函数,这就说明子类复用了父类的成员函数。


小结一下:子类可以复用父类的成员变量和成员函数。

1.2继承的定义  

1.定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类

2.继承关系和访问限定符

我们在以往的文章C++入门2——类与对象1(类的定义和this指针)中介绍了类的访问,当时介绍了类的三个访问限定符

但是现在我们要知道类也存在三种继承方式:

3.基类成员在派生类中的访问方式变化

C++入门2——类与对象1(类的定义和this指针)我们介绍类的三个访问限定符时说:(现阶段认为protected与private相同,不同点会在后面详细探究),所以我们有很长一段时间都没有用到protected,但在C++继承这里我们就用到了protected。

类成员/继承方式 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继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强。

把3拎出来通俗点说:

父类的public成员:public与三种继承方式相比“两者相比取其轻”:public继承,访问public成员;protected继承,访问protected成员;private继承,访问private成员。

父类的protected成员:同样的道理,protected与三种继承方式相比“两者相比取其轻”:public继承,访问protected成员;protected继承,访问protected成员;private继承,访问private成员。

父类的private成员:无论如何继承,子类都不能访问。

代码演示三种继承方式下父类成员的各类型成员访问关系的变化 :

给定父类:

class Person
{
public:
    void Print()
    {
        cout << _name << endl;
    }
protected:
    string _name = "张三"; // 姓名
private:
    int _age = 19;   // 年龄
};

①public继承:

class Student : public Person
{
 
protected:
    int _stunum = 2021060450; // 学号
};

即相当于:

②protected继承

class Student : protected Person
{
 
protected:
    int _stunum = 2021060450; // 学号
};

即相当于:

③private继承

class Student : private Person
{
 
protected:
    int _stunum = 2021060450; // 学号
};

即相当于:

2.基类和派生类对象赋值转换

派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

但要注意基类对象不能赋值给派生类对象。

class Person
{
protected:
  string _name; // 姓名
  string _sex;  // 性别
  int _age;
  // 年龄
};
class Student : public Person
{
public:
  int stuid; // 学号
};
void Test()
{
  Student s;
  // 1.子类对象可以赋值给父类对象/指针/引用
  Person p = s;
  Person* pp = &s;
  Person& rp = s;
  //2.基类对象不能赋值给派生类对象
  s = p;//error
}

3.继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员显示访问)

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

4. 注意在实际中在继承体系里面最好不要定义同名的成员。

class Person
{
protected:
    string _name = "张三"; // 姓名
private:
    int _age = 19;   // 年龄
};
 
class Student : public Person
{
public:
    void Print()
    {
        cout << "姓名:" << _name << endl;
        cout << "年龄:" << _age << endl;
        cout << "学号:" << _stuid << endl;
    }
protected:
    int _stuid = 2021060450; // 学号
    int _age = 22;
};
 
int main()
{
    Student s;
    s.Print();
    return 0;
}

运行结果年龄为22,这就是因为基类和派生类都有独立的作用域,当成员变量名字相同时,优先使用本作用域的变量。

#include <iostream>
 
using namespace std;
 
 
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:
  void fun()
  {
    cout << "func()" << endl;
  }
};
class B :public A
{
public:
  void fun(int a)
  {
    A::fun();
    cout << "fun(int a)->" << a << endl;
  }
};
 
int main()
{
  B b;
  b.fun(10);
  b.A::fun();
  return 0;
}

相关文章
|
4月前
|
存储 安全 Java
c++--继承
c++作为面向对象的语言三大特点其中之一就是继承,那么继承到底有何奥妙呢?继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用,继承就是类方法的复用。
93 0
|
4月前
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
49 0
|
8月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
7月前
|
存储 分布式计算 编译器
C++入门基础2
本内容主要讲解C++中的引用、inline函数和nullptr。引用是变量的别名,与原变量共享内存,定义时需初始化且不可更改指向对象,适用于传参和返回值以提高效率;const引用可增强代码灵活性。Inline函数通过展开提高效率,但是否展开由编译器决定,不建议分离声明与定义。Nullptr用于指针赋空,取代C语言中的NULL。最后鼓励持续学习,精进技能,提升竞争力。
|
7月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
358 6
|
9月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
136 16
|
9月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
183 5
|
8月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
4月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
95 0