C++之“继承”
继续开始关于C++相关的内容。C++作为面向对象的语言,有三大特性:封装,继承,多态。
这篇文章我们开始学习:继承。
一、继承的概念和定义
1. 继承的概念
什么是继承呢?
字面意思理解来看:继承就是获得某个人的所有遗产。在这里我们可以理解为:一个类继承了父类的成员变量以及成员函数。
接下来是准确的介绍:继承机制是面向对象程序设计使代码可以复用的一个重要手段,它允许我们在原来的类上进行扩展,增加新的属性(成员变量)和方法(成员方法),从而生成一个新的类,称之为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的是函数层次的复用,而继承就是类设计层次的复用。【父类/基类:这个类里面放的是共有的属性和方法。派生类/子类:可以看作是在父类上的扩展】
以前我们都是函数层面的复用(在一个函数里调用另一个函数),现在是类设计层面的复用。接下来用例子进一步说明什么是继承:
红色圈住的部分是两个类一模一样的部分,它们的成员函数和成员变量都有很多相似,看起来比较冗余,对于两个类我们可以采取继承的方法:将这两个类里面相同的(重复的)成员提取出来,放到父类,再将不同的成员(各自的成员)分别放在各自的类中不动,紧接着让派生类继承父类。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
class common //共有成员
{
public:void identity(){std::cout << "void identity()" << std::endl;}
protected:std::string _name;std::string _address;std::string _tel;
private:int _age;
};class student :public common //学生的那个类
{
public:void study(){}
protected:int _stuid;
};class teacher :public common //老师的类
{
public:void teaching(){}
protected:int _title;};
int main()
{//实例化对象student s;teacher t;s.identity(); t.identity();return 0;
}
公共的成员都放到common类中,student和teacher都继承common,就可以复用这些成员,不需要重复定义,省去了很多麻烦。
2. 继承的定义
定义格式
class student :public common
//class 派生类/子类:继承方式 父类/基类
继承方式
从图中可以看出:共有三种继承方式,这三个关键字,既可以在类里取修饰成员变量和函数,还可以在此处继承。它既是继承方式,也是访问限定符。之前了解到的是:使用public(公有的),类里类外都可以访问,private(私有的),只能在类里面使用,类外面不可以。在这里继续了解protected:使派生类可以访问到基类的成员变量/成员方法
分析:
1> 基类的private成员在派生类中无论以什么样的方式去继承,它们在派生类中其实都是不可见的。这里的不可见指的是基类的私有成员它还是被继承到了派生类的对象中,但是在语法上限制了派生类对象(用private限制的基类的成员,它们是存在的,只是我们不可见),不管是在派生类里面还是在派生类外面都不可以去访问基类中用private访问限定符限定的成员的,只可以在基类中自己使用。
第一种
第二种:
2>如果想让基类成员在类外面不能被访问,但是可以在派生类中被访问,可以将这个成员的方位限定符设为protected。(从这里就可以看出来protected(保护限定符)其实就是为了继承才设计出来的。)
3> 基类中的私有成员在子类(派生类)或者类外面是绝对无法被访问的,那基类的其他成员在派生类中的访问方式是什么呢?是:Min(成员在基类的访问限定符,继承方式)(public >protected> private)
举例:在上图中,基类中的成员变量_age是被protected访问限定符限制的,派生类在这里是以public继承方式去继承基类的,public和protected中protected权限最小,所以_age就相当于是被访问限定符限制protected限定在派生类中
4>使用关键词class时默认的继承方式是private,使用struct关键字是默认的继承方式是public,不过最好还是显示的写出继承方式,以增加代码的可读性。
5>我们在实际运用中一般都是用public的继承方式去继承的,几乎很少看见使用protected / private这两种继承方式去继承的,因为protected / private继承下来的成员都只能在派生类里面去使用,实际中扩展维护性也不强。
3.继承类模板
在这里我们尝试通过继承一个vector类模板来实现栈这个结构
将stack作为vector的派生类来生成的,我们在模拟实现stack的成员方法时,是直接通过复用基类的成员方法来进行实现的。
注意点:
1. 继承的时候,不止要写类名vector,还要写模板参数<T>.
namespace hou
{template<class T> //这个是模板参数class stack :public vector<T>//类名是vector,模板参数是T{};
}
2. 基类是类模板时,需要指定一下类域
namespace hou
{template<class T> //这个是模板参数class stack :public vector<T>//类名是vector,模板参数是T{public:void push(const T& x){vector<T>::push_back(x); //不能直接写push_back(x);// 基类是类模板时,需要指定⼀下类域, 否则编译报错:error C3861: “push_back”: 找不到标识符 // 因为stack<int>实例化时,也实例化vector<int>了(但需要注意的是,这里只是实例化了stack和vector这两个模板中的默认构造函数,其余成员函数均未进行实例化操作)//模版是按需实例化,push_back等成员函数未实例化,所以找不到 }};
}
我们每次复用基类的成员方法时,我们都需要使用作用域运算符来指定类域,虽然按道理来说,我们实例化出来派生类了,基类应该也就实例化出来了,事实确实如此,但是基类并不是一把实例化出来的,他的有些成员还没实例化出来,这时候我们就需要自己手动指示类域进行实例化了(如果我们不指明的话,编译器就会报未声明的报错),这就是我们之前所说的按需实例化。
#include<stdio.h>
#include<iostream>
#include<vector>
using namespace std;namespace hou
{template<class T> //这个是模板参数class stack :public vector<T>{public:void push(const T& x){vector<T>::push_back(x); //不能直接写push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};
}int main()
{hou::stack<int> st;st.push(1);st.push(2);while (!st.empty()){std::cout << st.top() << " ";st.pop();}return 0;
}
二、基类和派生类之间的转换
public继承的派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫做切片或者是切割。寓意把派生类中基类的那一部分切割出来,基类指针或引用指向的是派生类中切出来的基类那部分。
再回顾一下之前的类型转换:
double d=1.1;
const int& i = d;
这里不可以int& i = d;因为d的整型部分先赋给临时变量(临时变量有常性,意思是用const修饰了,所以将临时变量给给i,i必须是const修饰的int类型的对象,代码第二行)
1. 派生类(子类)对象可以赋值给(基类)父类的对象,调用父类中的拷贝构造函数即可实现。
student sobj; //student是子类
person p = sobj; //person是父类
2. 派生类(子类)对象可以赋值给基类(父类)指针,就相当于是把子类中父类的那部分切割出来,pp指针就指向切割出来的那部分的地址
person* pp = &sobj;
3.派生类(子类)对象可以赋值给基类(父类)引用。(把子类中父类的那一部分切割出来,p它所引用的就是从子类中切割出来的那一部分)
在图片中,红线框里并没有写const,但是没有报错,这说明中间没有产生临时变量。这里是编译器的特殊处理:赋值兼容转换。
(1)父类对象不能赋值给子类对象,会编译报错(少赋值给多?x)
三、继承中的作用域
在C++中,基类与派生类是两个不同的作用域(派生类是由基类继承下来的,但它们两个的作用域不是同一个,是独立的两个作用域)
隐藏规则:
1. 如果派生类和基类中的成员同名,那派生类就会屏蔽基类的同名成员,这叫做隐藏。但如果想在派生类中使用(和派生类同名的成员),也是可以的,直接指明父类的类域即可(基类::基类成员)
2. 对于成员函数,只要两者的函数名相同,那么两者就会构造隐藏关系【意思是:如果两个函数名相同,但是参数不同(void fun()和void fun(int i)),这两个在继承的情况下是隐藏关系。】
还需要回顾一下:什么是重载(需要满足的一个条件是:这两个函数在同一个作用域)【函数重载是指在一个作用域内,允许多个同名函数的存在,只要它们的形式参数(包括参数的数量、类型或顺序)有所不同即可。这种机制使得同一个函数名称能够执行不同的操作。】
四、派生类的默认成员函数
默认的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?(派生类本质也是一种类,只不过它与基类有着千丝万缕的关系)
派生类的构造函数
普通的类:在默认生成的时候:成员变量分为:内置类型(如果我们不写,它不确定,有可能随机值,也有可能初始化为0;如果有缺省值,就会用缺省值)和自定义类型(会调用这个自定义类型的默认构造,如果自定义类型没有默认构造,需要显式去写它的构造,在初始化列表定义初始化自定义类型)
1. 对派生类进行构造时,要记得,派生类有自己独有的成员,还有在父类里边儿共有的成员,(我们可以将子类中继承下来的父类成员,当作一个整体对象)默认构造:派生类独有的(内置和自定义)+父类成员(调用自己的默认构造)(如果父类没有默认构造,就需要自己显示地去写)
(派生类的构造函数)必须(调用基类的构造函数初始化基类那一部分的成员),如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用
之前普通的类的情况:初始化列表中,按照声明的顺序进行初始化的,在这里是先初始化父类的。
派生类对象 初始化时,先调用基类构造,再调用派生类构造函数。(和析构相反)
拷贝构造函数
1. 如果不写拷贝构造,编译器默认生成的是什么样子的?
:子类成员(内置类型[默认拷贝构造的话是值拷贝]+自定义类型[默认构造的话会调用这个类型的拷贝构造])+父类成员(必须调用父类的拷贝构造)
默认拷贝构造完成的挺好,不需要我们插手。
那什么时候,派生类的拷贝构造需要自己写--->假设一个成员需要深拷贝
int* ptr = new int[10];
(指针是内置类型)内置类型的默认拷贝构造(浅拷贝)会造成两个指针指向同一个空间,析构的时候会崩溃。当需要深拷贝的时候,就需要自己写拷贝构造了。
接下来看,如何显式地写拷贝构造:
//父类中的拷贝构造 Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;} //子类中的拷贝构造 Student(const Student& s): Person(s), _num(s._num),_address(s._address){cout << "Student(const Student& s)" << endl;}
派生类对象的拷贝构造函数,必须调用基类的拷贝构造来完成拷贝初始化。
赋值重载
如果没有需要深拷贝的,就不用自己写了
int main()
{Student s1( 18,"202306","hou");Student s3(99, "39393", "jing");s1 = s3;return 0;
}
//父类Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p) //如果是自己给自己赋值的话,就直接返回自己就可以了,有效地节省了时间,大大地提高了运行效率_name = p._name;return *this;} //派生类Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s) {// 构成隐藏,所以需要显⽰调⽤Person::operator =(s); //调用父类的operator=()函数去给父类中的那一部分成员变量进行赋值操作_num = s._num;_address = s._address;}return *this;}
派生类的operator=()函数隐藏了基类的operator=()函数,所以需要我们去显示调用基类的operator=()函数,就必须要去指定基类的作用域,只有这样才能访问基类中的那个operator=()赋值构造函数
析构函数
析构时,必须确保先析构子,再析构父
派生类的析构函数会在被调用完成后,自动去调用基类的析构函数去清理基类成员【所以,不需要显示调用父类析构】。因为只有这样才能保证派生类对象(先清理派生类成员),(再去清理基类成员的顺序。
~student()
{_num = 0;_address=nullptr;person::~person();//我们在去调用父类的析构函数去清理父类中的成员变量时,我们一定要在这里指定作用域,因为析构函数在最后它都会被处理成desructor()这个函数,进而会构成隐藏。
}//我们在显示调用父类的析构函数时,一定要注意要等到将子类中的成员变量全部清理完了以后,再去调用父类的析构函数去清理父类中的那一部分成员变量,通过我们前面所学过的知识可知,先构造的后析构,由于person是被继承过来的,就相当于是先有person,再有student,因此后析构person(也就是person)。
因为多态中一些场景导致析构函数需要重写,而重写的条件之一就是函数名相同(这个写一篇博客会讲到,这里我们只需要明白析构函数会被进行特殊处理即可),那么编译器在这里会对析构函数进行一个特殊处理的操作,将析构函数名处理成destructor(),所以基类的析构函数在不加virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。
实现一个不能被继承的类
方法一:将构造函数私有化(C++98)
为什么将构造函数私有化就不能被继承了呢?
派生类的构造必须调用基类的构造函数,但是基类的构造函数被私有化private,派生类看不见就自然不能去调用了,进而就无法实例化出对象了。
方法二:final关键字
C++11中增加了一个final关键字,final修改了基类后,派生类就不可以继承基类了。
继承与友元
友元关系不能被继承
回顾:通过使用friend关键字,可以让(一个类或函数)访问(另一个类)中的私有成员(私有成员变量和私有成员函数)和保护成员。Display是父类的友元,那么在Display这个函数里,可以访问父类里的私有和保护成员。
友元关系不能被继承,简单理解:父类的朋友,并不是,子类的朋友。所以,在Display中,并不能访问子类的私有和保护成员。
继承与静态成员
这个地址不一样说明:非静态成员num的地址是不一样的,派生类继承了这个num变量,父类和派生类各有一份。
那静态成员呢?
回顾:
- 静态成员变量是属于整个类的,而不是某个具体对象的属性。静态成员变量只有一份副本存在于内存中。
- 声明方式: 在类内部使用 static 关键字声明静态成员变量。
- 初始化位置: 静态成员变量必须在类外部进行初始化
基类 定义了一个 static静态成员变量,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都是只有一个static成员实例。
到静态成员 _one 的地址是⼀样的 ,说明派生类和基类共用同一份静态成员。
公有的情况下,父类和派生类,指定类域都可以访问静态成员
多继承及其菱形继承的问题
首先我们先搞清楚,什么是单继承和多继承。
单继承: 一个派生类只有一个直接基类时,称这个继承关系为单继承
在这个图片中,它们的关系是单继承。PostGraduate只是间接继承了Person,PostGraduate的直接基类只有一个,就是Student。
一个派生类有两个及以上直接基类时,称这个继承关系为多继承
多继承对象在内存中的模型是:先继承的基类在前面,后面继承的基类在后面,派生类成员放到最后面。
比如:Assistant这个类同时继承了一个Student类和Teacher类,继承的父类之间分别用","隔开,Assistant这个类先继承的是Student类,后继承的是Teacher类。
class Assistant :public Student, public Teacher
菱形继承是多继承的一种特殊情况。
多继承很容易造成菱形继承。(如果这两个基类Student和Teacher,都有一个共同的基类,就会形成菱形继承)
菱形继承有不太好的方面:菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份,这样的话,会大大增加空间的消耗。
怎么看出来有两份呢?
从图中可以看出,派生类Student和Teacher当中,都有基类Person。有两份Person就会导致数据冗余(有两份,浪费空间)和二义性(有两份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;
}
虚继承
C++中的多继承虽然说在实际中可能应用挺广泛的,但是它应运而生的菱形继承所带来的问题有点多,这也是java中没有多继承的一个主要原因,但是C++对于之前已经创立的语法是不可以进行修改的,我们只能够向前兼容,于是我们就又创建了一个新的语法——虚继承。
虚继承就是在我们派生类继承基类的时候,在继承方式前加上一个关键字virtual即可。在C++中,虚继承(Virtual Inheritance)是一种用于解决多继承中重复继承问题的机制。其主要意义在于确保共同的基类只被继承一次,避免数据冗余和访问歧义。
class Teacher :virtual public Person//teacher类虚继承了person类
c++的语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有了虚拟菱形继承,底层实现就会很复杂,性能方面就也会有一些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之一,后来的一些编程语言中基本上都没有多继承。
组合和继承
- public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。
- 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
- 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为⽩箱复用(这里既然说到了这个白盒测试和黑盒测试,我们先来简单地了解一下相关的知识:1 黑盒测试:不了解底层实现,是从功能的角度进行测试的,2 白盒测试(相对于黑盒测试更难):了解底层实现(代码实现),从代码运行的逻辑角度进行测试)。术语“⽩箱”是相对可视性⽽⾔:在继承⽅式中,基类的内部细节对派⽣类可⻅ 。继承⼀定程度破坏了基类的封装,基类的改变,对派生类有很⼤的影响。派生类和基类间的依赖关系很强,耦合度⾼。
- 对象组合是类继承之外的另⼀种复⽤选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接⼝。这种复用⻛格被称为⿊箱复用(black-box reuse),因为对象的内部细节是不可⻅的。对象只以“⿊箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
- 优先使用组合,⽽不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的关系既适合用继承(is-a)也适合组合(has-a),就用组合。
相关文章:

C++之“继承”
继续开始关于C相关的内容。C作为面向对象的语言,有三大特性:封装,继承,多态。 这篇文章我们开始学习:继承。 一、继承的概念和定义 1. 继承的概念 什么是继承呢? 字面意思理解来看:继承就是…...

Webug4.0靶场通关笔记19- 第24关邮箱轰炸
目录 第24关 邮箱轰炸 1.配置环境 2.打开靶场 3.源码分析 4.邮箱轰炸 (1)注册界面bp抓包 (2)发送到intruder (3)配置position (4)配置payload (5)开…...

java CompletableFuture 异步编程工具用法1
1、测试异步调用: static void testCompletableFuture1() throws ExecutionException, InterruptedException {// 1、无返回值的异步任务。异步线程执行RunnableCompletableFuture.runAsync(() -> System.out.println("only you"));// 2、有返回值的异…...
缺乏实体人形机器人的主流高精度仿真方案
在缺乏实体人形机器人的情况下,可通过以下主流仿真方案实现高精度模拟(基于2025年最新技术): 一、基础建模工具链 MATLAB Robotics Toolbox • 通过连杆(Link)和关节(Joint)定义生物力学参数 • 示例代码创建简化模型:…...
基于STM32、HAL库的CP2104 USB转UART收发器 驱动程序设计
一、简介: CP2104是Silicon Labs公司推出的一款USB转UART桥接芯片,具有以下特点: USB 2.0全速兼容 集成USB收发器,无需外部电阻 支持UART数据传输,波特率从300bps到2Mbps 内置EEPROM可配置设备信息 支持RTS/CTS硬件流控制 3.3V I/O电平,内置5V至3.3V稳压器 紧凑的QFN-24…...
ERC-20与ERC-721:区块链代币标准的双星解析
一、代币标准的诞生背景 在以太坊生态中,代币标准是构建去中心化应用(DApps)的基石。ERC-20与ERC-721分别代表同质化与非同质化代币的两大核心标准,前者支撑着90%以上的加密资产流通,后者则开启了数字资产唯一性的新时…...
使用Go语言对接全球股票数据源API实践指南
使用Go语言对接全球股票数据API实践指南 概述 本文介绍如何通过Go语言对接支持多国股票数据的API服务。我们将基于提供的API文档,实现包括市场行情、K线数据、实时推送等核心功能的对接。 一、准备工作 1. 获取API Key 联系服务提供商获取访问密钥(替…...
经典密码学算法实现
# AES-128 加密算法的规范实现(不使用外部库) # ECB模式S_BOX [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0x…...
git 远程仓库管理详解
Git 的远程仓库管理是多人协作和代码共享的核心功能。以下是 Git 远程仓库管理的详细说明,包括常用操作、命令和最佳实践。 1. 什么是远程仓库? 远程仓库(Remote Repository):存储在网络服务器上的 Git 仓库࿰…...
ABP vNext + gRPC 实现服务间高速通信
ABP vNext gRPC 实现服务间高速通信 💨 在现代微服务架构中,服务之间频繁的调用往往对性能构成挑战。尤其在电商秒杀、金融风控、实时监控等对响应延迟敏感的场景中,传统 REST API 面临序列化负担重、数据体积大、通信延迟高等瓶颈。 本文…...

若依框架Ruoyi-vue整合图表Echarts中国地图标注动态数据
若依框架Ruoyi-vue整合图表Echarts中国地图 概述创作灵感预期效果整合教程前期准备整合若依框架1、引入china.json2、方法3、data演示数据4、核心代码 完整代码[毫无保留]组件调用 总结 概述 首先,我需要回忆之前给出的回答,确保这次的内容不重复&#…...
京东(JD)API 商品详情数据接口讲解及 JSON 示例
前言 京东开放平台提供了多种商品详情相关的 API 接口,开发者可以通过这些接口获取商品的详细信息。以下为接口调用方式及 JSON 返回数据的参考示例。 1. 接口调用方式 京东商品详情接口通常采用以下形式: 请求方式:GET/POST关键参数&…...

算法中的数学:约数
1.求一个整数的所有约数 对于一个整数x,他的其中一个约数若为i,那么x/i也是x的一个约数。而其中一个约数的大小一定小于等于根号x(完全平方数则两个约数都为根号x),所以我们只需要遍历到根号x,然后计算出另…...
Python实例题:Python获取喜马拉雅音频
目录 Python实例题 题目 python-get-ximalaya-audioPython 获取喜马拉雅音频脚本 代码解释 get_audio_info 函数: download_audio 函数: 主程序: 运行思路 注意事项 Python实例题 题目 Python获取喜马拉雅音频 python-get-ximala…...

[监控看板]Grafana+Prometheus+Exporter监控疑难排查
采用GrafanaPrometheusExporter监控MySQL时发现经常数据不即时同步,本示例也是本地搭建采用。 Prometheus面板 1,Detected a time difference of 11h 47m 22.337s between your browser and the server. You may see unexpected time-shifted query res…...

LaTeX印刷体 字符与数学符号的总结
1. 希腊字母(Greek Letters) 名称小写 LaTeX大写 LaTeX显示效果Alpha\alphaAαα, AABeta\betaBββ, BBGamma\gamma\Gammaγγ, ΓΓDelta\delta\Deltaδδ, ΔΔTheta\theta\Thetaθθ, ΘΘPi\pi\Piππ, ΠΠSigma\sigma\Sigmaσσ, ΣΣOmega\omeg…...

剥开 MP4 的 千层 “数字洋葱”:从外到内拆解通用媒体容器的核心
在当今数字化时代,MP4 格式随处可见,无论是在线视频、手机拍摄的短片,还是从各种渠道获取的音频视频文件,MP4 都占据着主流地位。它就像一个万能的 “数字媒体集装箱”,高效地整合和传输着各种视听内容。接下来&#x…...
深度解析语义分割评估指标:从基础到创新实践
一、为什么需要专业评估指标? 在医疗影像分析中,一个3mm的肿瘤漏检可能导致误诊;在自动驾驶场景下,5%的边界识别误差可能引发严重事故。这些真实案例揭示了语义分割评估指标的关键作用。本章将带您深入理解指标背后的数学原理与实践价值。 --- ## 二、基础指标全解析 #…...
浅谈 Shell 脚本编程中引号的妙用
在 Shell 脚本编程中,引号的使用是一项基础却至关重要的技能。无论是单引号、双引号还是不加引号,它们都会显著影响 Shell 对字符串、变量、特殊字符以及命令的解析方式。理解这些差异不仅能帮助开发者编写更健壮的脚本,还能避免因误解引发的…...

从彼得·蒂尔四象限看 Crypto「情绪变迁」:从密码朋克转向「标准化追求者」
作者:Techub 精选编译 撰文:Matti,Zee Prime Capital 编译:Yangz,Techub News 我又带着一篇受彼得蒂尔(Peter Thiel)启发的思想杂烩回来了。作为自封的「蒂尔学派」信徒,我常透过他…...

Java线程安全问题深度解析与解决方案
一、线程安全问题的本质 并发编程的核心挑战:当多个线程同时访问共享资源时,由于操作系统的抢占式调度特性,可能导致不可预期的结果。这种因非原子操作和竞态条件引发的数据不一致问题,称为线程安全问题。 二、经典线程安全问题案…...

Mybatis解决以某个字段存在,批量更新,不存在批量插入(高效)(一)
背景 在开发企业级应用时,我们经常需要处理批量数据的插入和更新操作。传统的逐条处理方式性能低下,而简单的REPLACE INTO或INSERT ... ON DUPLICATE KEY UPDATE在某些场景下又不够灵活。本文将介绍一种基于临时表的高效批量插入/更新方案,解…...
高效C/C++之九:Coverity修复问题:关于数组操作 和 内存操作
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 高效C/C之九:Coverity修复问题:关于数组操作 和 内存操作 目录 【关注我,后…...

【时时三省】(C语言基础)怎样定义和引用二维数组
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 有的问题需要用二维数组来处理。例如,有3个小分队,每队有6名队员,要把这些队员的工资用数组保存起来以备查。这就需要用到二维数组,如下图&…...
Ubuntu上安装MySQL 8并配置Navicat远程连接
1. 安装MySQL 8 sudo apt update sudo apt install mysql-server -y2. 启动MySQL服务 sudo systemctl start mysql sudo systemctl enable mysql3. 设置root密码 3.1 登录MySQL(默认无密码): sudo mysql3.2 修改root认证方式并设置密码&a…...

杨校老师竞赛课之C++备战蓝桥杯初级组省赛
目录 1. 灯塔 题目描述 输入描述 输出描述 输入样例1 输出样例1 输入样例2 输出样例2 数据说明 2. 子区间 题目描述 输入描述 输出描述 输入样例 输出样例 数据说明 3. 染色 题目描述 输入描述 输出描述 输入样例1 输出样例1 输入样例2 输出样例2 数据…...

Matlab 基于Hough变换的人眼虹膜定位方法
1、内容简介 Matlab220-基于Hough变换的人眼虹膜定位方法 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...

vfrom表单设计器使用事件机制控制字段显示隐藏
1. 使用表单设计器进行debug调试 依据 vform3.0开发者文档 https://www.ganweicloud.com/docs/6.1.0/pages/d3e6d9/ 对switch组件设置事件逻辑 调试中...

【Redis篇】linux 7.6安装单机Redis7.0(参数优化详解)
💫《博主主页》: 🔎 CSDN主页 🔎 IF Club社区主页 🔥《擅长领域》:擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控;并对SQLserver、NoSQL(MongoDB)有了…...

信号的概念及产生
信号的概念 信号(signal)是一种软件中断机制,用于通知进程发生了特定的事件。信号可以由系统、其他进程或进程自身发送。 在现实生活中,也有许多的信号,比如说:红绿灯、闹钟、上课铃、父母喊你回家吃饭等等…...