当前位置: 首页 > news >正文

【c++随笔12】继承

【c++随笔12】继承

  • 一、继承
    • 1、继承的概念
    • 2、3种继承方式
    • 3、父类和子类对象赋值转换
    • 4、继承中的作用域——隐藏
    • 5、继承与友元
    • 6、继承与静态成员
  • 二、继承和子类默认成员函数
    • 1、子类构造函数
  • 二、子类拷贝构造函数
    • 3、子类的赋值重载
    • 4、子类析构函数
  • 三、单继承、多继承、菱形继承
    • 1、单继承:一个子类只有一个直接父类,我们称这种继承关系为单继承。
    • 2、多继承:一个子类有两个或以上直接父类,我们称这种继承关系为多继承。
    • 3、菱形继承(Diamond Inheritance)是指在类继承关系中,存在一个派生类同时继承自两个直接或间接基类,并且这两个基类又共同继承自一个共同的基类,从而形成了菱形状的继承结构。
      • 3.1、菱形继承可能引发以下问题:
      • 3.2、为了解决菱形继承带来的问题,可以采用以下方法:
    • 4、继承和组合

原创作者:郑同学的笔记
原创地址:https://zhengjunxue.blog.csdn.net/article/details/131795289
qq技术交流群:921273910

C++ 是基于面向对象的程序,面向对象有三大特性 —— 封装、继承、多态。

一、继承

1、继承的概念

  • 继承(inheritance)机制是面向对象程序设计,使代码可以复用的最重要的手段。
  • 它允许程序员在保持原有类特性的基础上进行扩展,以增加功能。这样产生新的类,称为派生类。
  • 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
  • 以前我们接触的复用都是函数复用,而继承是类设计层次的复用。
#include <iostream>
#include<string>
using namespace std;
class Person {/* 共有的信息 */
public:void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}string m_name;int m_age;
};/* Student 公有继承了 Person */
class Student : public Person {string m_stuID;  // 学号
};/* Teacher 公有继承了 Person */
class Teacher : public Person {string m_employeeID;  // 工号
};int main() 
{Person p;p.print();Student su;su.print();Teacher t;t.print();return 0;
}

输出

在这里插入图片描述

  • 继承的定义格式
    Student 是 子类,我们也称之为派生类。Person 是父类,我们也称之为 基类。
class 派生类名:[继承方式] 基类名{派生类新增加的成员
};

2、3种继承方式

  • 访问限定符:public / protected / private

下表汇总了不同继承方式对不同属性的成员的影响结果

继承方式/基类成员public成员protected成员private成员
public继承publicprotected不可见
protected继承protectedprotected不可见
private继承privateprivate不可见

由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public

3、父类和子类对象赋值转换

  • 子类对象可以赋值给父类的对象、父类的指针、父类的引用:
#include <iostream>
#include<string>
using namespace std;
class Person {/* 共有的信息 */
public:void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}string m_name;int m_age;
};/* Student 公有继承了 Person */
class Student : public Person {string m_stuID;  // 学号
};int main() 
{Student s;Person p = s;Person* pointer_p = &s;Person& ref_p = s;p.print();pointer_p->print();ref_p.print();return 0;
}

输出

在这里插入图片描述

注意事项:

  • ① 父类对象不能赋值给子类对象
Student s;  // 子类
Person p;   // 父类s = p;
  • ② 父类的指针可以通过强转赋值给子类的指针,但是必须是父类的指针是指向子类对象时才是安全的。

这里父类如果是多态类型,可以使用 RTTI(Run-Time Type Information,即运行时类型识别)的 dynamic_cast 来进行识别后进行安全转换。

#include <iostream>
#include<string>
using namespace std;
class Person {/* 共有的信息 */
public:void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}string m_name;int m_age;
};/* Student 公有继承了 Person */
class Student : public Person {
public:string m_stuID;  // 学号
};int main() 
{Student s;// 父类的指针可以通过强制类型转换赋值给子类的指针Person* pointer_p = &s;Student* pointer_s = (Student*)pointer_p;pointer_s->m_stuID;Person p;// 这种情况虽然可以,但是会存在越界访问问题Person* pointer_p2 = &p;Student* pointer_s2 = (Student*)pointer_p2;pointer_s->m_stuID;return 0;
}

4、继承中的作用域——隐藏

  • 继承体系中的父类和子类都有独立的作用域,如果子类和父类有同名成员,
  • 此时子类成员会屏蔽父类对同名成员的直接访问,这种情况叫做 “隐藏” (有文章把它叫重定义,其实我不建议这种叫法,因为重定义指的是同一个作用域重复定义)。

在子类成员函数中,可以使用如下方式进行显式访问:
基类::基类成员

例如:在Student类中
Person::print()

#include <iostream>
#include<string>
using namespace std;
class Person {/* 共有的信息 */
public:void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}string m_name;int m_age;
};/* Student 公有继承了 Person */
class Student : public Person {
public:void print(){cout << "name = " << m_name << "\nage = " << m_age <<"\nstuID = "<< m_stuID << endl;}string m_stuID;  // 学号
};int main() 
{Student s;s.print();return 0;
}

5、继承与友元

  • 友元关系不能继承,也就是说父类友元不能访问子类私有和保护成员!
#include <iostream>
#include<string>
using namespace std;class Student;
class Person {
public:friend void Display(const Person& p, const Student& s);/* 共有的信息 */protected:void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}string m_name;int m_age;
};/* Student 公有继承了 Person */
class Student : public Person {protected:void print(){cout << "name = " << m_name << "\nage = " << m_age <<"\nstuID = "<< m_stuID << endl;}string m_stuID = "110";  // 学号
};void Display(const Person& p, const Student& s)
{cout << p.m_name << endl;cout << s.m_stuID << endl;
}int main() 
{Person p;Student s;Display(p, s);return 0;
}

报错
“Student::m_stuID”: 无法访问 protected 成员(在“Student”类中声明)

6、继承与静态成员

  • 父类定义了 static 静态成员,则整个继承体系里面中有一个这样的成员。

可以理解为共享,父类的静态成员可以在子类共享,父类和子类都能去访问它。
无论派生出多少个子类,都只有一个 static 成员实例:

#include <iostream>
#include<string>
using namespace std;class Person {
public:Person() {++m_count;}void print(){cout << "name = "<<m_name << "\nage = "<<m_age << endl;}
protected:string m_name;int m_age;
public:static int m_count;
};int Person::m_count = 0;
/* Student 公有继承了 Person */
class Student : public Person 
{
protected:string m_stuID = "110";  // 学号
};int main() 
{Student s1;Student s2;Student s3;Person s;cout << "大家都可以访问" << endl;cout << "人数 : " << Person::m_count << endl;cout << "人数 : " << Student::m_count << endl;cout << "大家也都可以变动" << endl;s3.m_count = 0;cout << "人数 : " << Person::m_count << endl;cout << "并且他们的地址也都是一样的,因为所有继承体系中只有一个" << endl;cout << "人数 : " << &Person::m_count << endl;cout << "人数 : " << &Student::m_count << endl;return 0;
}

输出

在这里插入图片描述

二、继承和子类默认成员函数

1、子类构造函数

  • 子类的构造函数必须调用父类的构造函数初始化父类的那一部分成员。

如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。(如下面的demo)

  • 子类对象初始化先调用父类构造再调子类构造。
#include <iostream>
#include<string>
using namespace std;class Person {
public:Person(const char* m_name = "hello") {cout<<"构造 Person \n";}
protected:string m_name;int m_age;
};class Student : public Person 
{
public:Student(const char* name, int stuID) :Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。m_stuID(stuID){cout << "构造 Student \n";}
protected:int m_stuID;  // 学号
};int main() 
{Student s1("",18);return 0;
}

思考:如何设计一个不能被继承的类?

  • 将父类的构造函数私有化:
class A 
{
private:A() {}
};

父类的构造函数私有化后,在父类的外部,父类自己也没法初始化了?

(单例模式可以这么做,如下)


class A {
public:static A CreateObject() {  // 提供一个获取对象的方式return A();}
private:A() {}
};class B : public A {};int main(void) 
{A a = A::CreateObject();return 0;
}

二、子类拷贝构造函数

  • 子类的拷贝构造函数必须调用父类的拷贝构造完成拷贝初始化。
#include <iostream>
#include<string>
using namespace std;class Person {
public:Person(const char* m_name = "hello") {cout<<"构造 Person \n";}Person(const Person& p):m_name(p.m_name){cout << "拷贝构造 Person \n";}
protected:string m_name;int m_age;
};class Student : public Person 
{
public:Student(const char* name, int stuID) :Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。m_stuID(stuID){cout << "构造 Student \n";}Student(const Student& s):Person(s), //子类的拷贝构造函数必须调用父类的拷贝构造完成拷贝初始化。m_stuID(s.m_stuID){cout << "拷贝构造 Student \n";}
protected:int m_stuID;  // 学号
};int main() 
{Student s1("haha",18);Student s2(s1);return 0;
}

输出

在这里插入图片描述

3、子类的赋值重载

  • 子类的 operator= 必须要调用父类的 operator= 完成父类的复制。
#include <iostream>
#include<string>
using namespace std;class Person {
public:Person(const char* m_name = "hello") {cout<<"构造 Person \n";}Person& operator=(const Person& p){cout << "赋值重载 Person \n";if(this != &p){m_name = p.m_name;}return *this;}
protected:string m_name;int m_age;
};class Student : public Person 
{
public:Student(const char* name, int stuID):Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。m_stuID(stuID){cout << "构造 Student \n";}Student& operator=(const Student& s){cout << "赋值重载 Student \n";if (this != &s){Person::operator=(s); //子类的 operator= 必须要调用父类的 operator= 完成父类的复制。m_stuID = s.m_stuID;}return *this;}
protected:int m_stuID;  // 学号
};int main() 
{Student s1("小白",18);Student s2("小黑", 18);s1 = s2;return 0;

输出

在这里插入图片描述

4、子类析构函数

  • 为了保证子类对象先清理子类成员再清理父类成员的顺序,先子后父。

子类析构先子后父,子类对象的析构清理是先调用子类析构再调父类析构。

  • 子类析构函数完成后会自动调用父亲的析构函数,所以不需要我们显式调用。
#include <iostream>
#include<string>
using namespace std;class Person {
public:Person(const char* m_name = "hello") {cout<<"构造 Person \n";}~Person(){cout << "析构 Person \n";}
protected:string m_name;int m_age;
};class Student : public Person 
{
public:Student(const char* name, int stuID):Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。m_stuID(stuID){cout << "构造 Student \n";}~Student(){cout << "析构 Student \n";}
protected:int m_stuID;  // 学号
};int main() 
{Student s1("小白",18);return 0;
}

输出

在这里插入图片描述

三、单继承、多继承、菱形继承

1、单继承:一个子类只有一个直接父类,我们称这种继承关系为单继承。

2、多继承:一个子类有两个或以上直接父类,我们称这种继承关系为多继承。

3、菱形继承(Diamond Inheritance)是指在类继承关系中,存在一个派生类同时继承自两个直接或间接基类,并且这两个基类又共同继承自一个共同的基类,从而形成了菱形状的继承结构。

下面是一个示例代码来说明菱形继承的概念:

class Animal {
public:void eat() {cout << "Animal eats." << endl;}
};class Mammal : public Animal {
public:void run() {cout << "Mammal runs." << endl;}
};class Bird : public Animal {
public:void fly() {cout << "Bird flies." << endl;}
};class Bat : public Mammal, public Bird {
public:void sleep() {cout << "Bat sleeps." << endl;}
};

在上述代码中,Animal 是基类,Mammal 和 Bird 是直接派生类,而 Bat 是通过多重继承同时派生自 Mammal 和 Bird 的派生类。注意到 Mammal 和 Bird 都继承自 Animal,这就形成了菱形继承结构。

3.1、菱形继承可能引发以下问题:

  • 二义性(Ambiguity):由于 Bat 同时继承自 Mammal 和 Bird,如果两个基类都定义了相同的成员函数或变量,编译器就无法确定该使用哪个版本,从而导致二义性错误。
  • 冗余数据:由于两个基类都继承自同一个基类 Animal,当 Bat 对象被创建时,会在内存中存在两份相同的 Animal 的数据。

3.2、为了解决菱形继承带来的问题,可以采用以下方法:

  • 使用虚拟继承(Virtual Inheritance):在 Mammal 和 Bird 继承 Animal 时,使用 virtual 关键字表示虚拟继承,这样就可以消除冗余数据和二义性问题。
class Mammal : virtual public Animal {// ...
};class Bird : virtual public Animal {// ...
};
  • 使用间接继承:在 Bat 类中只直接继承 Mammal 或 Bird 的一个,而间接继承另一个基类的成员函数或变量。
class Bat : public Mammal {
private:Bird bird;
public:// 使用 bird 对象来访问 Bird 类中的成员
};

菱形继承是多重继承中的一种特殊情况,需要谨慎使用,并采取适当的解决方案来避免引发问题。

4、继承和组合

  • 继承和组合 public继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种 has-a 的关系。假设B组合了A,每个B对象中都有一个A对象。
  • 优先使用对象组合,而不是类继承 。
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用 (white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。 继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关 系很强,耦合度高。
  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对 象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse), 因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系, 耦合度低。优先使用对象组合有助于你保持每个类被封装。
  • 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适 合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

class A {// ...
};// 继承
class B : public A {};class C {// ...
};// 组合
class D {C _c;
};

继承就是团体出行,A 任何成员的修改都有可能影响 B 的实现。
组合就是自由出行,C 只要不修改公有,就不会对 D 有影响。

相关文章:

【c++随笔12】继承

【c随笔12】继承 一、继承1、继承的概念2、3种继承方式3、父类和子类对象赋值转换4、继承中的作用域——隐藏5、继承与友元6、继承与静态成员 二、继承和子类默认成员函数1、子类构造函数 二、子类拷贝构造函数3、子类的赋值重载4、子类析构函数 三、单继承、多继承、菱形继承1…...

Excel中使用数据验证、OFFSET实现自动更新式下拉选项

在excel工作簿中&#xff0c;有两个Sheet工作表。 Sheet1&#xff1a; Sheet2&#xff08;数据源表&#xff09;&#xff1a; 要实现Sheet1中的“班级”内容&#xff0c;从数据源Sheet2中获取并形成下拉选项&#xff0c;且Sheet2中“班级”内容更新后&#xff0c;Sheet1中“班…...

Android修行手册 - 可变参数中星号什么作用(冷知识)

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…...

Python与ArcGIS系列(三)视图缩放

目录 0 简述1 在所有图层中缩放至所选要素2 在单独图层中缩放至所选要素3 改变地图范围0 简述 本篇介绍如何利用arcpy实现缩放视图到所选要素以及改变地图范围功能。 对于以及创建的选择集数据,通常需要进行缩放以更好地显示所选要素,要素缩放可分为两种:第一种是在所有图层…...

[ASP]数据库编辑与管理V1.0

本地测试&#xff1a;需要运行 ASP专业调试工具&#xff08;自己搜索下载&#xff09; 默认登陆口令&#xff1a;admin 修改口令&#xff1a;打开index.asp找到第3行把admin"admin"改成其他&#xff0c;如admin"abc123" 程序功能齐全&#xff0c;代码精简…...

MyBatis Plus整合Redis实现分布式二级缓存

MyBatis缓存描述 MyBatis提供了两种级别的缓存&#xff0c; 分别时一级缓存和二级缓存。一级缓存是SqlSession级别的缓存&#xff0c;只在SqlSession对象内部存储缓存数据&#xff0c;如果SqlSession对象不一样就无法命中缓存&#xff0c;二级缓存是mapper级别的缓存&#xff…...

如何帮助 3D CAD 设计师实现远程办公

当 3D CAD 设计师需要远程办公时&#xff0c;他们可能需要更强的远程软件&#xff0c;以满足他们的专业需求。比如高清画质&#xff0c;以及支持设备重定向、多显示器支持等功能。3D CAD 设计师如何实现远程办公&#xff1f;接下来我们跟随 Platinum Tank Group 的故事来了解一…...

如何在 Idea 中修改文件的字符集(如:UTF-8)

以 IntelliJ IDEA 2023.2 (Ultimate Edition) 为例&#xff0c;如下&#xff1a; 点击左上角【IntelliJ IDEA】->【Settings…】&#xff0c;如下图&#xff1a; 从弹出页面的左侧导航中找到【Editor】->【File Encodings】&#xff0c;并将 Global Encoding、Project E…...

【C++】单例模式【两种实现方式】

目录 一、了解单例模式前的基础题 1、设计一个类&#xff0c;不能被拷贝 2、设计一个类&#xff0c;只能在堆上创建对象 3、设计一个类&#xff0c;只能在栈上创建对象 4、设计一个类&#xff0c;不能被继承 二、单例模式 1、单例模式的概念 2、单例模式的两种实现方式 …...

php的api接口token简单实现

<?php // 生成 Token function generateToken() {$token bin2hex(random_bytes(16)); // 使用随机字节生成 tokenreturn $token; } // 存储 Token&#xff08;这里使用一个全局变量来模拟存储&#xff09; $tokens []; // 验证 Token function validateToken($token) {gl…...

CCNA课程实验-13-PPPoE

目录 实验条件网络拓朴需求 配置实现基础配置模拟运营商ISP配置ISP的DNS配置出口路由器OR基础配置PC1基础配置 出口路由器OR配置PPPOE拨号创建NAT(PAT端口复用) PC1测试结果 实验条件 网络拓朴 需求 OR使用PPPoE的方式向ISP发送拨号的用户名和密码&#xff0c;用户名&#xf…...

cocosCreator 之 Bundle使用

版本&#xff1a; v3.4.0 语言&#xff1a; TypeScript 环境&#xff1a; Mac Bundle简介 全名 Asset Bundle(简称AB包)&#xff0c;自cocosCreator v2.4开始支持&#xff0c;用于作为资源模块化工具。 允许开发者根据项目需求将贴图、脚本、场景等资源划分在 Bundle 中&am…...

分类网络搭建示例

搭建CNN网络 本章我们来学习一下如何搭建网络&#xff0c;初始化方法&#xff0c;模型的保存&#xff0c;预训练模型的加载方法。本专栏需要搭建的是对分类性能的测试&#xff0c;所以这里我们只以VGG为例。 请注意&#xff0c;这里定义的只是一个简陋的版本&#xff0c;后续一…...

为 Ubuntu 虚拟机构建 SSH 服务器

以校园网环境和VMware为例&#xff0c;关键步骤如下&#xff1a; 安装 SSH 服务&#xff1a; 打开 Ubuntu 虚拟机。打开终端。输入命令 sudo apt-get update 更新软件包列表。输入命令 sudo apt-get install openssh-server 安装 SSH 服务。 配置 SSH 服务&#xff1a; 编辑配…...

SpringBoot--中间件技术-2:整合redis,redis实战小案例,springboot cache,cache简化redis的实现,含代码

SpringBoot整合Redis 实现步骤 导pom文件坐标 <!--redis依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>yaml主配置文件&#xff0c;配置…...

linux rsyslog配置文件详解

1.rsyslog配置文件简介 linux rsyslog配置文件/etc/rsyslog.conf分为三部分:MODULES、GLOBAL DIRECTIVES、RULES ryslog模块说明 模块说明MODULES指定接收日志的协议和端口。若要配置日志服务器,则需要将相应的配置项注释去掉。GLOBAL DIRECTIVES主要用来配置日志模版。指定…...

wordpress是什么?快速搭网站经验分享

​作者主页 &#x1f4da;lovewold少个r博客主页 ⚠️本文重点&#xff1a;c入门第一个程序和基本知识讲解 &#x1f449;【C-C入门系列专栏】&#xff1a;博客文章专栏传送门 &#x1f604;每日一言&#xff1a;宁静是一片强大而治愈的神奇海洋&#xff01; 目录 前言 wordp…...

排序 算法(第4版)

本博客参考算法&#xff08;第4版&#xff09;&#xff1a;算法&#xff08;第4版&#xff09; - LeetBook - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 本文用Java实现相关算法。 我们关注的主要对象是重新排列数组元素的算法&#xff0c;其中每个元素…...

asp.net 在线音乐网站系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 在线音乐网站系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 asp.net 在线音乐网站系统1 应用…...

ElastaticSearch -- es之Filters aggregation 先过滤再聚合

使用场景 使用es时&#xff0c;有时我们需要先过滤后再聚合&#xff0c;但如果直接在query的filter中过滤&#xff0c;不止会影响到一个聚合&#xff0c;还会影响到其他的聚合结果。 比如&#xff0c;我们想要统计深圳市某个品牌的总销售额&#xff0c;以及该品牌的女款衣服的…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟

2025年4月29日&#xff0c;在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上&#xff0c;可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞&#xff0c;强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...