C++基础语法----类的继承

简介: C++基础语法----类的继承

前言


前面介绍到了类的封装,想必大家对类的封装都有了很好的理解,今天简单说一下类的继承。继承的基本概念以及如何进行继承。



一、子类的与基类的关系:


  • 子类可以看成特殊的基类;---------------【比基类多一些新的属性或方法】
  • 子类拥有父类的成员属性与函数-------【基类公有的、被保护的可以直接用,私有的以函数为跳板可以间接使用】
  • 子类的对象可以当作基类对象使用----【与前两个性质无异】
  • 子类可以有父类没有的方法与属性----【可以定义新的属性与方法扩展】


二、继承的方式与权限:


1.基类属性子类使用权限


继承父类的方法与属性,如果在父类中是私有属性,那么无论何种属性继承在子类中都不能使用;
打个比方;
父亲的公有属性就是父亲的姓名; public            【儿子可以知道】
父亲的被保护成员就是家里面的银行密码; protected     【儿子可以共享】
父亲的私有成员就是父亲的爱人; private       【儿子不可以与父亲共享】
                        【有些秘密不可以让儿子知道】


2.继承权限


公有继承: 将基类的属性与方法继承下来;权限不变;
(最常使用):只不过基类的私有权限可以继承但是不能使用;
保护继承: 对与基类中被保护的和公有的属性,方法全部改为被保护;私有的仍为私有;
私有继承: 将父类中的属性与方法以私有权限继承下来;但父类的私有权限仍只能在父类中使用; 
         子类继承下来的成员方法,只能在子类中使用;


三、static关键字在基类时,如果属性是公有,那么也可以被子类进行共享:


需要注意的是:类中的static变量要进行显示的初始化;
初始化语法是: 类型 static 类名::变量名=XXX;(其实是在告诉编译器给他分派内存)


四、继承的构造与析构:


1 构造函数调用顺序


基类>组合类>自己;(如果级别相同那么顺序由定义顺序决定)
析构与构造相反;
• 1
• 2


2 如果基类没有不含参的构造函数,那么必须在构造子类对象时对其初始化


   语法(子类的构造函数对父类属性进行初始化):  
   子类名 (参数列表) :基类1名(参数列表),基类2名)(参数列表){函数体};
  ⚠️:在继承中子类仅仅将父类的属性与方法继承了下来,千万不要认为
     子类的对象上面还有一个父类;
  ⚠️:子类对象与父类对象的关系:(符号代表属性与变量)
  ***  爷爷类//代代相传代代变强变多【只有三颗*】
  ***@@@  爸爸类         【比爷爷类多出三个@】
  ***@@@$$$ 儿子类       【儿子类有更多的属性与方法】


3 子类初始化基类属性的方法


两个b是同一个b,可以直接初始化父类的属性
只有在基类没有无参构造函数的时候这么写
可以多传几个参数,看个人需求。参数要在子类的构造函数参数列表内。

cecfb9a9c08b4bd5828602f6efff6f04.png


4 示例代码


//继承中的构造与析构函数
//构造函数调用顺序是  基类>嵌套类>自己,析构相反
#include<iostream>
using namespace std;
class A {
private:
  int a;
public:
  A(int a ) {
    this->a = a;
    cout << "这里是A的构造函数" << endl;
  }
  void print() {
    cout << "A: "<<a << endl;
  }
  ~A() {
    cout << "这里是A的析构函数" << endl;
  }
};
class B :public A{
private:
  int b;
public:
  B(int b=0) :A(b){//要在B构造函数的初始化列表对A进行初始化,
    this->b = b;
    cout << "这里是B的构造函数" << endl;
  }
  ~B() {
    cout << "这里是B的析构函数" << endl;
  }
};
int main_01() {
  B b1(2);
  b1.print();//b1对象既有类A的属性,又有类B的属性;输出的是B类的print
  b1.A::print();//输出类A的print;
  return 0;
}


五、赋值兼容原则:


子类可以初始化父类:
   初始化时调用父类的拷贝构造函数;
   父类的指针与引用可以指向子类;
   子类有父类没有的方法,并且可以完成父类的工作;
值得注意的是:
  在基类的指针或者引用与子类的对象关联时就好像指针只与子类对象
  的一部分进行了关联;仅仅是用子类对象的一部分属性
  初始化出来了基类的对象,并且该对象中没有子类的成员属性;


具体实现方法如下:


//基类的指针与引用可以指向子类的对象;
//子类的对象可以对父类进行初始化;
#include<iostream>
using namespace std;
class A1 {
public:
  int a;
  int b;
public:
  void print() {
    cout << "A1 A1:"<<" : "<<a<<b << endl;
  }
};
class B1:public A1 {
public:
  int a;
  int b;
public:
  B1(int a, int b) {
    this->a = a;
    this->b = b;
    A1::a = 100;
    A1::b = 200;
  }
  void print() {
    cout << "    " << B1::a<<" "<< B1::b << endl;
    cout << "B1 B1"<<" : "<<a<<b << endl;
  }
  int getA() {
    return  a;
  }
};
int main_02() {
  B1 b1(1, 2);
  A1 a1= b1;//(兼容赋值原则)(子类直接给父类的对象赋值)
  A1* p = NULL;
  p = &b1;//将子类的地址赋给基类的指针;
  A1& q = b1;//基类还可以做子类的引用;
//  B1* q = NULL;
//  q = &a1;也不可以进型子类的指针指向基类的对象
//  B1 b2 = a1;可以将子类的对象赋值给父类的对象:不能反向操作;因为子类对象有父类没有的东西;
//************************分割线*******************************************************************
  cout << "" << endl;
  cout << q.a << endl;
  q.print();//打印的还是A类的东西;
  cout << "-----------------------------0-----" << endl;
  p->print();//输出的是A类内的元素;并且调用的是A类的打印函数;
  cout << p->a << endl;//打印A类的元素
  b1.print();//只有这句话调用了b的打印函数
  a1.print();//没有显示的对a1成员属性进行操作:但输出了100  200;如果将a1=b1去掉则输出乱码:
  //由此可知b1中的A1::a与A1::b对a1中的a,b进行了初始化;
  return 0;
}


六、成员属性或者成员方法重名问题:


子类与父类难免有些成员属性会重名;由于有作用域的限制子类继承时可以将同名的属性与方法继承下来;


只需在使用时稍加注意即可:


默认情况子类在调用重名属性时会先调用自己的属性;

语法:子类对象名.重名函数;

想要调用基类的是重名属性的方法:(使用域作用符):

语法:子类对象名.基类名::重名属性;【::是域作用符】



七、多继承:


在C++中一个类可以继承多个类;并且拥有他们的属性与方法;
但有时候会产生二义性
• 1
• 2


情况一、虚继承解决构造函数的二义性


ABCD四个类继承如图所示: 
               A      //A是BC的基类
               /    \ //BC继承A后可能分别会以不同的方式初始化A
                    【假设A没有无参构造函数】必须由B或者C进行初始化
             B       C  
               \    /
                D   //D在同时继承BC,如果初始化D的对象时,
                      调用B还是C的构造函数去初始化A?
这就使编译器十分矛盾;产生了二义性;
解决方法;利用虚继承:virtual关键字在继承时进行限定;
【讲多态的时候会着重讲virtual关键字】


具体实现方法如下:


#include<iostream>
using namespace std;
class AA {
public:
  int a;
  AA(int a = 0) {
    this->a = a;
  }
  void print() {
    cout << " AA" << endl;
  }
};
class BB:virtual public AA {//
public:
  int b;
  BB(int b = 0):AA(1) {
    this->b = b;
  }
  void print() {
    cout << "BB" << endl;
  }
};
class CC:virtual public AA {//
public:
  int c;
  CC(int c = 0):AA(2) {
    this->c = c;
  }
  void print() {
    cout << "CC" << endl;
  }
};
class DD :public BB,public CC{
public:
  int d;
  //初始化继承到的基类
  DD(int d=0):BB(d),CC(d),AA(d) {//如果不是虚继承 那么不可以直接用DD去初始化AA
    this->d = d;
  }
  void ptint() {
    cout << "DD" << endl;
  }
};
int main() {
  DD d1(666);
  cout << "b  c  d" << endl << d1.b << d1.c << d1.d << endl;
  cout << d1.a << endl;
//  cout << d1.a << endl;//不明确A是哪个里面的---------解决方案---虚继承virtual
  cout << d1.AA::a << endl;//没有虚继承时输出了BB对象的属性
  cout << d1.BB::a << endl;//虚继承后输出的均是最后一次对该成员变量的操作的值
  cout << d1.CC::a << endl;//virtual会对其自动进行改变
  cout << "BB  " << sizeof(BB) << "CC  " << sizeof(CC) << "DD  " << sizeof(DD) << endl;//虚继承输出12 12 24
  return 0;//不虚继承输出 8 8 20;
}


用virtual关键字处理后,类的大小有所改变;具体原因在多态中


情况二、基类重名变量


A         B  //C同时继承了AB,但AB中有一变量名相同,
          \    /
          C   //在C通过域作用符调用AB中的同名变量时;会产生二义性;



解决方法:


在C的对象调用那个变量时加上AB的作用符进行限定;
比如使用A中的aa属性调用方法则为
          C.A::aa
• 1
• 2
• 3


总结


这些仅仅是C++类继承中的一些浅显的知识点,希望大家能够熟练掌握,至少要知道继承的几种权限,以及继承来的属性是否可以使用,还有虚继承、多继承的几种问题。

相关文章
|
2天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
2天前
|
编译器 C语言 C++
☺初识c++(语法篇)☺
☺初识c++(语法篇)☺
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
50 13
|
1月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
58 16
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
52 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
40 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4
|
1月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
32 3
|
3月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
89 2