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

【C++】——继承详解

目录

1、继承的概念与意义

2、继承的使用

2.1继承的定义及语法

2.2基类与派生类间的转换

2.3继承中的作用域

2.4派生类的默认成员函数

<1>构造函数

<2>拷贝构造函数

<3>赋值重载函数

<4析构函数

<5>总结

3、继承与友元

4、继承与静态变量

5、菱形继承及菱形虚拟继承

6、继承与组合


1、继承的概念与意义

什么是继承?

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。

继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。

通过继承联系在一起的类构成了一种层次关系,在这种关系中有一个基类(base class),其他类则是直接或间接地从基类继承过来的,这些继承来的类可以称为派生类(drived class)。基类通常有着层次关系中所有类共同拥有维护的成员,而每个派生类也有着自己各自特定的成员。

一个简单的例子:一个学习管理系统,那么成员必定有学生,老师等等,这些是身份,归根到底是个人(基类)包含着名字、年龄、地址等基础信息。这些需要共同维护的就是基类的成员。

//共同维护的成员部分->基类
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "qsy"; // 姓名 string _address; // 地址 string _tel; // 电话 int _age = 18; // 年龄 
};class Student : public Person
{
public:// 学习 void study(){// ...}
protected:int _stuid; // 学号 
};
class Teacher : public Person
{
public:// 授课 void teaching(){//...}
protected:string title; // 职称 
};
int main()
{Student s;Teacher t;s.identity();t.identity();return 0;
}

可以看到派生类可以访问基类成员

如果没有继承这种结构关系的话 Student和Teacher 都有姓名/地址/ 电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。更好地体现了继承是类设计层次的复用。

2、继承的使用

2.1继承的定义及语法

这就是继承的语法格式

继承方式与访问限定符号一样有着三种,不同的继承方式与不同的类成员组合会是不同的情况

总结一下规律:

<1>基类 private 成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

将年龄变为私有验证一下是否继承到了派生类对象

可以看到继承下来了但是不可以访问!!

<2>如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为 protected。可以看出保护成员限定符是因继承才出现的。

如果想要访问 private 成员可以在基类中成员函数访问,这样派生类可以间接访问到 private成员

<3>基类的私有成员在派生类都是不可见。基类的其他成员在派生类的访问方式 == Min(成员在基类的访问限定符,继承方式),public  > protected > private。

Tip:class默认继承方式是 private,struct默认继承方式是public。最好显示写出继承方式

<4>在实际运用中⼀般使用都是 public 继承,几乎很少使用 protetced/private 继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实 际中扩展维护性不强。

2.2基类与派生类间的转换

基类与派生类之间是否有着类型的转换呢?

答案是可以的! public继承中有一个 is-a 概念:每个派生类都是一个特殊的基类对象

• public继承的派生类对象 可以赋值给 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中基类那部分切出来,基类指针或引用指向的是派生类中切出来的基类那部分。

基类对象不能赋值给派生类对象

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type  Information)的dynamic_cast 来进行识别后进行安全转换。

2.3继承中的作用域

继承体现中也有各自的作用域规则并且引出来一个隐藏概念,隐藏影响的只是编译器查找规则

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

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

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

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

// Student的_num 和 Person的_num 构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆 
class Person
{
protected:string _name = "小徐"; // 姓名 int _num = 111; // ⾝份证号 
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份证号:" << Person::_num << endl;cout << " 学号:" << _num << endl;}
protected:int _num = 999; // 学号 
};
int main()
{Student s1;s1.Print();return 0;
};

访问的是哪个 _num 呢?

可以看到派生类成员隐藏了基类的同名成员,直接访问了派生类的 _num 

同理,函数也有隐藏的现象

A和B类中的 fun 两个函数构成什么关系呢??根据前面的知识可以知道继承体系中函数名相同就构成隐藏关系

2.4派生类的默认成员函数

6个默认成员函数,默认的意思就是指我们不写,编译器会自动生成⼀个,那么在派生类中,这 几个成员函数是如何生成的呢?

四个常见默认成员函数:

<1>构造函数

派生类的构造函数必须调用基类的构造函数初始化基类的那⼀部分成员。

class Person
{
public:Person(const char* name="xxc") //全缺省函数,默认构造:_name(name){cout << "Person()" << endl;}
protected:string _name;//姓名
};class Student :public Person
{
public://不显示实现默认构造,编译器生成的// 1. 内置类型->不确定// 2. 自定义类型->调用自定义类型的显示写的默认构造// 3. 基类成员看作一个整体,要求调用基类的默认构造
protected:int _num;//学号string _addrss;//地址
};int main()
{Student s1;return 0;
}

如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用

写一个 Student的构造函数

还是报错!前面提到 需要把基类成员当成一个对象调用基类的构造函数

如何实现一个不能被继承的类呢?

方法1:基类的构造函数私有,派生类的构成必须调用基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。

方法2:C++11新增了⼀个final关键字,final 修改基类,派生类就不能继承了。

<2>拷贝构造函数

派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

可以看到,没有资源申请的时候 Student 并不需要自己显示实现拷贝构造,因为编译器默认拷贝构造会调用基类的拷贝构造

那么怎么自己实现拷贝构造呢?(Tip:基类对象是最先声明(内存顺序)的,初始化列表中第一个初始化)

Person(const Person& p): _name(p._name)
{cout << "Person(const Person& p)" << endl;
}Student(const Student& s):Person(s),_num(s._num),_addrss(s._addrss)
{//深拷贝
}

Person(s) 这个 s 是派生类对象的引用为什么可以传给基类呢? 涉及基类与派生类间的转换概念——切片 

如果显示写了拷贝构造但是不显示调用基类的拷贝构造的会,编译器会自动调用默认构造而非调用基类的拷贝构造

补充一下缺省值构成默认构造,运行一下发现调用的就是默认构造而非拷贝构造

<3>赋值重载函数

派生类的operator=必须要调用基类的operator=完成基类的复制。

赋值重载与拷贝构造类似一般编译器默认生成的就已经够用了,如果有资源申请的话才需要显示实现

Student& operator=(const Student& s)
{if (this != &s){operator=(s);//派生类切片基类成员_num = s._num;_addrss = s._addrss;}return *this;
}

栈溢出,无限递归调用,我们不是想要调用基类的赋值函数吗?为什么调用了派生类的呢?

需要注意的是派生类的 operator= 隐藏了基类的operator= ,所以显示调用基类的operator= ,需要指定基类作用域

Student& operator=(const Student& s)
{if (this != &s){//基类和派生类的赋值构成了隐藏关系 需要指定作用域Person::operator=(s);//派生类切片基类成员_num = s._num;_addrss = s._addrss;}return *this;
}

<4析构函数

析构函数可以显示调用,那么可以在派生类显示调用基类的析构函数来清理基类成员

可是为什么调不动呢?这里派生类和基类的析构函数构成了隐藏关系

因为多态中⼀些场景析构函数需要构成重写,重写的条件之一是函数名相同,那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。

想要调用就标明作用域:

Person::~Person()

但是像上述这样写,会有一个问题,基类的析构会调用两次!!!

其实,派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。所以我们不必在派生类的析构函数中进行调用基类的析构函数,不然就会重复释放同一块空间,导致报错!

可以怎么理解派生类析构自动调用基类的析构呢? 先子后父保证析构顺序!显示调用不一定保证先子后父的析构顺序

<5>总结

派生类和基类的层次关系逻辑基础还是类和对象

派生类的默认成员函数的注意事项:

<1>派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。


<2>派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。


<3>派生类的operator=必须要调用基类的operator=完成基类的复制。


<4>派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员(不需要显示和调用基类析构)。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。


<5>派生类对象初始化先调用基类构造再调派生类构造。派生类对象析构清理先调用派生类析构再调基类的析构。


<6>因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系

3、继承与友元

友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员。比如爸爸的朋友可以说是你的朋友吗?


class Student;//前置声明
class Person
{
public:friend void Display(const Person& p, const Student& s);//需要前置声明否则报错招不到 Student
protected:string _name; // 姓名 
};
class Student : public Person
{
protected:int _stuNum; // 学号 
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;
}
int main()
{Person p;return 0;
}

如果访问派生类的私有和保护成员呢?

可以看见是不可访问的 在派生类同样设置一个友元就可以解决这个问题了。

4、继承与静态变量

基类定义了 static 静态成员,则整个继承体系里面只有⼀个这样的成员。无论派生出多少个派生类,都只有⼀个static 成员实例。

验证一下:

class A
{
public: static int _a;int _aa;
};
class B :public A
{
public:int _b;
};
// static int _a = 1;报错int A::_a = 1;//注意定义的方式
int main()
{A  a;B b1;B b2;//这⾥的运行结果可以看到非静态成员_aa的地址是不⼀样的// 说明派生类继承下来了,⽗类派生类对象各有⼀份 cout << &a._aa << endl;cout << &b1._aa << endl;cout << endl;// 这⾥的运行结果可以看到静态成员 _a 的地址是⼀样的 //说明派生类和基类共用同⼀份静态成员 cout << &a._a << endl;cout << &b1._a << endl;cout << &b2._a << endl;cout << endl;//公有情况下 基类派生类都可以访问静态成员变量cout << a._a << endl;cout << b1._a << endl;cout << b2._a << endl;return 0;
}

也就说明他们共用一个_a变量,所以无论派生出多少个子类,都只有一个static成员实例

这个特性可以带来一种思路统计实例化类的数量个数,只需在构造函数中加入一个增加该静态变量的语句即可:

class Person
{
public:Person() { ++_count; }//子类的构造会调用父类构造
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};
int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};
int main()
{Student s1;Student s2;Student s3;cout << " 人数 :" << Person::_count << endl;Student::_count = 0;cout << " 人数 :" << Person::_count << endl;return 0;
}

这样我们就可以知道该继承体系中实例化了多少个类了!!!

5、菱形继承及菱形虚拟继承

首先声明一下,由于C++的历史缘故,其一致行走在语言发展的前端,一直在尝试新的内容。在发展过程中,有些内容加入到C++的时候,还没有发现其弊端。而后来发现的时候,为了向上兼容,只能打补丁,所以不开避免的不会有一些弊端,会有复杂的语法和复杂的特性。总要有先驱者走前前面,而C++就是!!!

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

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

菱形继承:菱形继承是多继承的⼀种特殊情况。

菱形继承的问题,从上面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。

class Person
{
public:string _name; // 姓名 
};
class Student : public Person
{
protected:int _num; //学号 
};
class Teacher : public Person
{
protected:int _id; // 职⼯编号 
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程 
};
int main()
{// 编译报错:error C2385: 对“_name”的访问不明确 二义性Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

那该如何解决数据冗余的问题呢??可以借用虚拟继承!!

虚拟继承(virtual)可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在StudentTeacher的继承Person时使用虚拟继承,即可解决问题。

这是什么原理呢?测试一下!

菱形继承不虚拟继承的情况

#include<iostream>
#include<string>using namespace std;class A
{
public:int _a;
};class B : public A
//class B : virtual public A
{
public:int _b;
};class C : public A
//class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

调试一下:

通过这个逐语句调试的内存变化,我们可以确定大致的内存情况:

不使用虚拟继承就是这样的内存情况,也好理解为什么同名变量的两份是如何储存的了。
接下来我们来看虚拟继承下的菱形继承是怎么个情况:

内存分布:

a储存在最下面,而B,C部分的原有储存_a的位置现在是什么呢???
其实是个指针,那我们来看看指针指向的空间储存着什么吧:

???怎么对应位置是00 00 00 00为什么是零?往下看看:
分别储存着16进制数字14 0c转换为10进制数字20 12,然后对应B,C原本的指针位置(006FFB6C)加上这个值(偏移量),都会指向到A _a的空间!!!这个00 00 00 00到多态的部分再来进行讲解,知道原地址加上下面的值就是A _a的空间就可以了!!!

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
即原本B,C中_a的位置储存这一个指针,指针指向的位置有一个偏移量,原位置的地址加上偏移量就会指向A的空间!!!

那这样进行拷贝切片的时候是怎样的呢?一样是把D中B对象的部分切片,然后通过虚基表的方式来找到_a。但这样也带来了一些代价:(PS:内存中的储存顺序就是声明的顺序,先继承谁,谁就在前面)

多继承指针偏移问题(切片)

p1和p2指向哪里呢???

内存分布中,先继承的放前面!

因为切片的概念p2指向 base2开始但是只能看见 base2 那一部分

6、继承与组合

  • public继承是一种is-a(谁是什么)的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种has-a(谁有什么)的关系。假设B组合了A,每个B对象中都有一个A对象(也就是把A作为B的成员变量)
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse 能看见,不安全,耦合度高)。术语 “白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。


  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse 不能能看见,安全,耦合度低),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

有关继承的经典面试题
<1>C++有多继承,为什么java等语言没有?
历史原因!C++是先驱者(人的直觉认为多继承很合理,我感觉正常人都会想到多继承),并且c++中的多继承处理起来十分复杂,访问基类变量的过程就会很复杂!!!java等后来发展的语言见到c++中多继承的复杂,就干脆放弃了。

<2>什么是菱形继承?多继承的问题是什么?
菱形继承如字面意思(两个父类的父类是同一个类就会发生菱形继承),多继承本身没什么问题,真正的问题是有多继承就可能发生菱形继承。菱形继承就有问题了:变量的二义性和继承冗杂。解决办法很简单就是虚拟继承,但是这样就会大大降低效率。

<3>继承和组合的区别?什么时候用继承?什么时候用组合?
继承:通过扩展已有的类来获得新功能的代码复用方法
组合:新类由现有类的对象合并而成的类的构造方式

如果二者间存在一个“是”的关系,并且一个类要对另外一个类公开所有接口,那么继承是更好的选择
如果二者间存在一个“有”的关系,那么首选组合
!能用组合就用组合!!!能用组合就用组合!!!能用组合就用组合!!!

相关文章:

【C++】——继承详解

目录 1、继承的概念与意义 2、继承的使用 2.1继承的定义及语法 2.2基类与派生类间的转换 2.3继承中的作用域 2.4派生类的默认成员函数 <1>构造函数 <2>拷贝构造函数 <3>赋值重载函数 <4析构函数 <5>总结 3、继承与友元 4、继承与静态变…...

RocketMQ 消费方式

在消息传递系统中&#xff0c;“推&#xff08;Push&#xff09;”和“拉&#xff08;Pull&#xff09;”是两种不同的消息消费方式&#xff0c;RocketMQ 也支持这两种模式。下面是对这两种模式的详细解释&#xff1a; 1. 推模式&#xff08;Push Model&#xff09; 模式简介…...

初始爬虫7

针对数据提取的项目实战&#xff1a; 补充初始爬虫6的一个知识点&#xff1a; etree.tostring能够自动补全html缺失的标签&#xff0c;显示原始的HTML结构 # -*- coding: utf-8 -*- from lxml import etreetext <div> <ul> <li class"item-1">…...

深入理解Appium定位策略与元素交互

深入理解Appium定位策略与元素交互 在移动应用测试领域&#xff0c;Appium作为一款流行的跨平台自动化测试工具&#xff0c;其强大而灵活的元素定位能力对于构建稳定、高效的测试脚本至关重要。本文将深入探讨Appium支持的各种定位方法&#xff0c;并分享如何通过高级技巧和最…...

java基础面试题总结

java基础面试题总结 目录 前言 1. JVM vs JDK vs JRE的了解 2. 谈谈你对编程、编译、运行的理解 3. 什么是字节码?采用字节码的好处是什么? 5. java中的注解有几种&#xff0c;分别是什么&#xff1f; 6. 字符型常量和字符串常量 7.标识符和关键字的认识 8. 泛型&#xff…...

Typescript 的类型断言

类型断言&#xff08;Type Assertion&#xff09;是 TypeScript 中的一种机制&#xff0c;允许开发者手动指定某个值的类型&#xff0c;而不是让 TypeScript 自动推断类型。类型断言通常用于在编译时告诉 TypeScript 编译器某个值的具体类型&#xff0c;以便在后续代码中进行类…...

【设计模式】单例模式详解及应用实例

单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;保证一个类在整个程序的生命周期中只有一个实例&#xff0c;并提供一个全局访问点。单例模式广泛用于需要全局唯一实例的场景&#xff0c;比如数据库连接池、日志对象、线程池等。 单例模式…...

学习图解算法 使用C语言

图解算法 使用C语言 也就是通过C语言实现各种算法 链接&#xff1a;百度云盘 提取码&#xff1a;1001...

基于Netty实现TCP客户端:封装断线重连、连接保持

文章目录 引言I 基于Netty实现TCP客户端基于 Netty 创建客户端 时序图封装思路NettyClient 封装II 客户端的断线重连本质使用过程中断线重连重试策略III 心跳机制心跳检测处理器心跳机制实现逻辑IV 同步等待消息返回V 工具ForkJoinPoolByteConvertUtilsee also处理假死把handle…...

基于形状记忆聚合物的折纸超结构

​ 公众号端文章&#xff1a; 基于SMP的折纸超结构https://mp.weixin.qq.com/s?__bizMzkwMjc0MTE3Mw&mid2247484016&idx4&sn16f8d4aaaff76d776cec19bc0adbdd3b&chksmc0a1afaaf7d626bc0457d9cc4ba1b38424c2aad71ffec548715e47f5611cf00f10d5a511f3b3#rd 折…...

前端用html写excel文件直接打开

源码 <html xmlns:o"urn:schemas-microsoft-com:office:office" xmlns:x"urn:schemas-microsoft-com:office:excel" xmlns"http://www.w3.org/TR/REC-html40"> <head><meta charset"UTF-8"><!--[if gte mso 9]&…...

FastText 和 Faiss 的初探了解

概览 大模型目前已经是如火如荼的程度&#xff0c;各个大厂都有推出面向大众的基础大模型&#xff0c;同时诸多行业也有在训练专有大模型&#xff0c;而大模型的发展由来却是经过多年从文本检索生成、深度学习、自然语言处理&#xff0c;在Transformer架构出来后&#xff0c;才…...

微服务保护学习笔记(五)Sentinel授权规则、获取origin、自定义异常结果、规则持久化

文章目录 前言4 授权规则4.1 基本原理4.2 获取origin4.3 配置授权规则 5 自定义异常结果6 规则持久化 前言 微服务保护学习笔记(一)雪崩问题及解决方案、Sentinel介绍与安装 微服务保护学习笔记(二)簇点链路、流控操作、流控模式(关联、链路) 微服务保护学习笔记(三)流控效果(…...

YOLOv8目标检测模型——遥感小目标检测经验分享

小目标检测——YOLOV8 一、引言 背景介绍 &#xff08;1&#xff09;目标检测的重要性 目标检测在许多领域都具有极其重要的作用。在自动驾驶中&#xff0c;目标检测能够识别道路上的障碍物和行人&#xff0c;确保行车安全。在视频监控中&#xff0c;目标检测能够实时发现异…...

构建响应式 Web 应用:Vue.js 基础指南

构建响应式 Web 应用&#xff1a;Vue.js 基础指南 一 . Vue 的介绍1.1 介绍1.2 好处1.3 特点 二 . Vue 的快速入门2.1 案例 1 : 快速搭建 Vue 的运行环境 , 在 div 视图中获取 Vue 中的数据2.2 案例 2 : 点击按钮执行 vue 中的函数输出 vue 中 data 的数据2.3 小结 三 . Vue 常…...

计算机毕业设计选题推荐-在线投票系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...

【C/C++】程序的构建(编译)过程概述

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:C: 探索C编程精髓&#xff0c;打造高效代码仓库 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、前言 二、预处理&#xff08;Preprocessi…...

ElasticSearch-2-核心语法集群高可用实战-Week2

ES批量操作 1.批量获取文档数据 这里多个文档是指&#xff0c;批量操作多个文档&#xff0c;搜索查询文档将在之后的章节讲解 批量获取文档数据是通过_mget的API来实现的 (1)在URL中不指定index和type 请求方式&#xff1a;GET 请求地址&#xff1a;_mget 功能说明 &#…...

STM的CAN通信学习

显性电平&#xff1a;0 隐性电平&#xff1a;1 一、帧结构 1.帧类型 1&#xff09;数据帧&#xff1a;发送设备主动发送数据&#xff08;广播式&#xff09; 2&#xff09;请求帧&#xff1a;接收设备主动请求数据&#xff08;请求式&#xff09; 2.帧结构 1&#xff…...

【高等数学学习记录】函数

【高等数学&学习记录】函数 从事测绘工作多年&#xff0c;深刻感受到基础知识的重要及自身在这方面的短板。 为此&#xff0c;打算重温测绘工作所需基础知识。练好基本功&#xff0c;为测绘工作赋能。 1 知识点 1.1 函数 设数集 D ⊂ R D\subset R D⊂R&#xff0c;称映射…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...