第六层:继承(上)

简介: 第六层:继承(上)

前情回顾


在第五层中,我遇到了在C++中第二个重载,操作符重载,它与函数重载差别很大,对于操作符重载,本质其实为函数,只是将函数名换成了C++提供给程序员特定的名字,同时第五层的力量也掌握到了,要踏入第六层了…


继承


当脚接触到第六层的地面的时候,那到声音随之飘来:“这第六层可不简单,它的力量是面向对象的第二大特性——继承,希望你不要让我失望…”


🚄上章地址:第五层:C++中的运算符重载


继承的作用


继承是面向对象的三大特性之一,在C++中,有些类的关系如下图:

0a2653c851af460fa595bd959398a8f1.png

可以发现,这些类,下级的成员除了会拥有上级的特性,还拥有自己的特性,这个时候如果一个一个封装,会造成很多重复的代码,这里就可以用继承的力量,减少大量的重复代码。


继承的基本语法


在浏览一些网站的时候,比如:csdn、牛客,都会发现,它们都有公共的头部和底部,那我自己的csdn主页来说:


0eacb84100b54626af849e6b562bf92a.png0eacb84100b54626af849e6b562bf92a.png


不一样的是中间的内容不相同,那如果用普通方法打印一下这个网页的基本内容会是什么样的?


#include<iostream>
using namespace std;
class A1
{
public:
  void head()
  {
  cout << "博客主页头部" << endl;
  }
  void middle()
  {
  cout << "A1的博客内容" << endl;
  }
  void  bottom()
  {
  cout << "博客主页底部" << endl;
  }
};
class A2
{
public:
  void head()
  {
  cout << "博客主页头部" << endl;
  }
  void middle()
  {
  cout << "A2的博客内容" << endl;
  }
  void  bottom()
  {
  cout << "博客主页底部" << endl;
  }
};
class A3
{
public:
  void head()
  {
  cout << "博客主页头部" << endl;
  }
  void middle()
  {
  cout << "A2的博客内容" << endl;
  }
  void  bottom()
  {
  cout << "博客主页底部" << endl;
  }
};
void test1()
{
  A1 a1;
  A2 a2;
  A3 a3;
  a1.head(); a1.middle(); a1.bottom();
  cout << "=======================" << endl;
  a2.head(); a2.middle(); a2.bottom();
  cout << "=======================" << endl;
  a3.head(); a3.middle(); a3.bottom();
}
int main()
{
  test1();
  return 0;
}

0a2653c851af460fa595bd959398a8f1.png

每个都封装成类,看到是可以进行正常的输出的,但是,可以发现,重复代码很多,这些是不必要的,那现在就可以试试继承的写法,继承的基本语法为:


class 子类 : 继承方式 父类

{

子类的特点

}


子类也可以被叫做派生类

父类也可以被叫做基类

普通用法中,可以将重复的代码,封装到父类当中,将每个子类特别的成员封装在子类里面,那继承方式是什么?


继承方式


继承方式一共有三种:


1.公共继承(public)

2.保护继承(protected)

3.私有继承(private)


0eacb84100b54626af849e6b562bf92a.png

公共继承

父类所有权限不变继承到子类当中,但是不能访问私有权限的成员

保护继承

父类除了私有权限的其他权限都变成保护权限继承到子类当中,私有权限内成员依然不能访问

私有继承

父类中所有权限变成私有权限继承到子类当中,私有权限内成员依然访问不到

那了解了继承方式后,怎么用继承的方式写出上面的网页呢:


#include<iostream>
using namespace std;
class A
{
public:
  void head()
  {
  cout << "博客主页头部" << endl;
  }
  void  bottom()
  {
  cout << "博客主页底部" << endl;
  }
};
class A1:public A
{
public:
  void middle()
  {
  cout << "A1的博客内容" << endl;
  }
};
class A2 :public A
{
public:
  void middle()
  {
  cout << "A2的博客内容" << endl;
  }
};
class A3: public A
{
public:
  void middle()
  {
  cout << "A2的博客内容" << endl;
  }
};
void test1()
{
  A1 a1;
  A2 a2;
  A3 a3;
  a1.head(); a1.middle(); a1.bottom();
  cout << "=======================" << endl;
  a2.head(); a2.middle(); a2.bottom();
  cout << "=======================" << endl;
  a3.head(); a3.middle(); a3.bottom();
}
int main()
{
  test1();
  return 0;
}


0a2653c851af460fa595bd959398a8f1.png

0a2653c851af460fa595bd959398a8f1.png


继承中的对象模型


从父类继承过来的成员,那些属于子类?在继承方式当中提到,私有权限是访问不到的?那是否继承到了子类当中?


#include<iostream>
using namespace std;
class A
{
public:
  int _a;
protected:
  int _b;
private:
  int _c;
};
class A1 :public A
{
public:
  int _d;
};
void test1()
{
  A1 a1;
  cout << sizeof(a1) << endl;
}
int main()
{
  test1();
  return 0;
}


0a2653c851af460fa595bd959398a8f1.png

0a2653c851af460fa595bd959398a8f1.png

可以看到,父类内三个int占12,子类一个int占4,加起来正好是16,而输出的结果也是16,说明,父类内所有非静态成员会被继承到子类当中,只是父类内私有权限的成员被编译器藏起来了,不能进行访问,但是确实继承到了子类当中。


继承中的构造和析构顺序


那对于构造和析构呢?是先有的父类还是子类?可以试着用代码进行验证:


#include<iostream>
using namespace std;
class A
{
public:
  A()
  {
  cout << "父类构造调用" << endl;
  }
  ~A()
  {
  cout << "父类析构调用" << endl;
  }
};
class A1:public A
{
public:
  A1()
  {
  cout << "子类构造调用" << endl;
  }
  ~A1()
  {
  cout << "子类析构调用" << endl;
  }
};
void test1()
{
  A1 a1;
}
int main()
{
  test1();
  return 0;
}

0a2653c851af460fa595bd959398a8f1.png

可以看到,构造为先有父后由子,析构则反过来。


继承中同名成员访问


非静态成员


当子类和父类中出现了非静态同名成员,如何通过子类对象,访问子类或者父类中的非静态同名成员呢?


子类:直接访问

父类:需要加作用域

这里用代码进行验证:


#include<iostream>
using namespace std;
class A
{
public:
  void a()
  {
  cout << "父类中调用" << endl;
  }
};
class A1:public A
{
public:
  void a()
  {
  cout << "子类中调用" << endl;
  }
};
void test1()
{
  A1 a1;
  a1.a();
  a1.A::a();
}
int main()
{
  test1();
  return 0;
}

0a2653c851af460fa595bd959398a8f1.png

成员属性的调用也与函数调用一样,可以用代码来进行验证:


#include<iostream>
using namespace std;
class A
{
public:
  A()
  {
  _a = 10;
  }
  void a()
  {
  cout << "父类中调用" << endl;
  }
  void a(int a)
  {
  cout << "父类第二个函数调用" << endl;
  }
  int _a;
};
class A1:public A
{
public:
  A1()
  {
  _a = 20;
  }
  void a()
  {
  cout << "子类中调用" << endl;
  }
  int _a;
};
void test1()
{
  A1 a1;
  cout << a1._a << endl;
  cout << a1.A::_a << endl;
}
int main()
{
  test1();
  return 0;
}


0eacb84100b54626af849e6b562bf92a.png


那如果在父类中,发生函数重载,子类能直接调用参数不同的那个函数吗?


、#include<iostream>
using namespace std;
class A
{
public:
  void a()
  {
  cout << "父类中调用" << endl;
  }
  void a(int a)
  {
  cout << "父类第二个函数调用" << endl;
  }
};
class A1:public A
{
public:
  void a()
  {
  cout << "子类中调用" << endl;
  }
};
void test1()
{
  A1 a1;
  a1.a(10);
  a1.A::a();
}
int main()
{
  test1();
  return 0;
}


2d65d23f6d4748949b924e4057485923.png

2d65d23f6d4748949b924e4057485923.png

编译器直接报错,程序是跑不起来的,这里是因为,在子类中出现与父类同名的成员函数,子类的同名函数会直接隐藏掉父类中所有的同名成员函数。


相关文章
|
6月前
|
存储 数据采集 机器学习/深度学习
新闻聚合项目:多源异构数据的采集与存储架构
本文探讨了新闻聚合项目中数据采集的技术挑战与解决方案,指出单纯依赖抓取技术存在局限性。通过代理IP、Cookie和User-Agent的精细设置,可有效提高采集策略;但多源异构数据的清洗与存储同样关键,需结合智能化算法处理语义差异。正反方围绕技术手段的有效性和局限性展开讨论,最终强调综合运用代理技术与智能数据处理的重要性。未来,随着机器学习和自然语言处理的发展,新闻聚合将实现更高效的热点捕捉与信息传播。附带的代码示例展示了如何从多个中文新闻网站抓取数据并统计热点关键词。
251 2
新闻聚合项目:多源异构数据的采集与存储架构
|
9月前
|
安全 云计算
服务器系统资源不足怎么办
服务器系统资源不足怎么办
405 4
|
存储 SQL 关系型数据库
(十四)全解MySQL之各方位事无巨细的剖析存储过程与触发器!
前面的MySQL系列章节中,一直在反复讲述MySQL一些偏理论、底层的知识,很少有涉及到实用技巧的分享,而在本章中则会阐述MySQL一个特别实用的功能,即MySQL的存储过程和触发器。
273 0
|
11月前
|
JSON Java 数据格式
【微服务】SpringCloud之Feign远程调用
本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
604 1
|
11月前
|
Java API Apache
除了 Maven,还有哪些工具可以管理项目的依赖和版本冲突
除了Maven,常用的项目依赖管理和版本冲突解决工具有Gradle、Ivy、Ant+Ivy、SBT等。这些工具各有特点,适用于不同的开发环境和需求。
865 2
|
存储 监控 数据处理
Flink⼤状态作业调优实践指南:Datastream 作业篇
本文整理自俞航翔、陈婧敏、黄鹏程老师所撰写的大状态作业调优实践指南。
56988 5
Flink⼤状态作业调优实践指南:Datastream 作业篇
|
XML 存储 JSON
C# | 使用Json序列化对象时忽略只读的属性
将对象序列化成为Json字符串是一个使用频率非常高的功能。Json格式具有很高的可读性,同时相较于XML更节省空间。 在开发过程中经常会遇到需要保存配置的场景,比如将配置信息保存在配置类型的实例中,再将这个对象序列化成为Json字符串并保存。当需要加载配置时,则是读取Json格式的字符串再将其还原成配置对象。在序列化的过程中,默认会将所有公开的属性和字段都序列化进入Json字符串中,这其中也会包含只读的属性或字段,而只读的属性和字段在反序列化的过程中其实是无意义的,也就是说这一部分存储是多余的。 本文将讲解如何在执行Json序列化时,忽略掉那些只读的属性和字段。
361 0
C# | 使用Json序列化对象时忽略只读的属性
|
NoSQL 关系型数据库 MySQL
分布式任务调度的几种实现
【2月更文挑战第2天】本文主要介绍了分布式任务调度的几种实现,使用Redis实现分布式锁方案,使用MySQL实现任务调度,开源框架 XXL-JOB等方案,最后需要考虑到负载均衡的问题。
298 1
|
JavaScript
vue实现多个el-form表单提交统一校验的2个方法
vue实现多个el-form表单提交统一校验的2个方法
938 0
|
NoSQL IDE Linux
Linux的学习之路:8、Linux调试器-gdb使用
Linux的学习之路:8、Linux调试器-gdb使用
113 0

热门文章

最新文章