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

C++ 继承,多态

看前须知:

本篇博客是作者听课时的笔记,不喜勿喷,若有疑问可以评论区一起讨论。

继承

定义:

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

继承格式

继承基类成员访问格式

1.基类(父类)的private成员在派生类(子类)中无论是以什么样的继承方式都是不可见的,但可以通过基类的成员函数进行访问。

2.protected:派生类(子类)可以继承且访问基类(父类)的成员函数,但是其他类中不能被访问。

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

4.在实际运用中一般使用都是public继承

应用(利用继承实现栈)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
using namespace std;
//用继承模拟实现栈
//#define CONTAINER std::vector
#define CONTAINER std::list
namespace lph {template<class T>class stack :public CONTAINER<T> {public:void push(const T& x) {//基类是类模板,需要指定类域否者编译报错CONTAINER<T>::push_back(x);}void pop() {CONTAINER<T>::pop_back();}T& top() {return CONTAINER<T>::back();}size_t size() {return CONTAINER<T>::size();}bool empyt() {return CONTAINER<T>::empty();}};
}
int main() {lph::stack<int> s1;//进行实例化时调用模板的实例化,但并不会实例化基类(父类)的成员函数,所有在调用基类(父类)的成员函数时需要指定类域。s1.push(1);s1.push(2);s1.push(3);s1.push(4);while (!s1.empyt()) {cout<<s1.top()<<" ";s1.pop();}return 0;
}

基类和派⽣类间的转换

public继承的派⽣类对象,可以赋值给基类(父类)的指针/基类的引⽤。这⾥有个形象的说法叫切⽚或者切 割。

寓意把派⽣类中基类那部分切出来,基类指针或引⽤指向的是派⽣类中切出来的基类那部分(父类不能赋值给子类)

继承中的作⽤域

隐藏规则

1.继承体系中基类(父类)和派生类(子类)都有独立的作用域

2.派生类(子类)和基类(父类)中有同名成员,派生类将屏蔽基类对基类对同名成员的直接访问(在派生类成员函数中,可以使用基类::基类成员 显示访问)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class person {
protected:int num = 999;//同名成员变量
};
class student : public person {
public:void print() {cout << num << endl;//访问到的是派生类的成元变量100cout<<person::num<<endl;//访问基类成员变量999}
protected:int num = 100;//同名成员变量
};
int main() {student s1;s1.print();return 0;
}

 3.对于成员函数的隐藏规则,只要派生类中有与基类中同名函数就会进行隐藏。

using namespace std;
class person {
public:void fun() {cout << "fun()" << endl;}
};
class student : public person {
public:void fun(int i) {cout << "fun(int i)" << endl;}
};int main() {student s1;s1.fun(1);//带参数的直接进行访问派生类s1.person::fun();//不带参数需要指定类域return 0;
}
//只要子类有函数名与基类函数名像似就把基类进行隐藏

 4.在实际继承体系中,最好不要定义同名成员或则成员函数。

派⽣类的默认成员函数

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

类和对象中默认成员函数规则。

1.对于内置类型是否初始化不确定。

2.对与自定义类型会调用他的默认构造。

3.派生类中的默认成员函数调用其父类的默认成员函数

对应规则

1.默认构造

派⽣类的默认构造函数必须调⽤基类的默认构造函数初始化基类的那⼀部分成员。如果基类没有默认的构造 函数,则必须在派⽣类构造函数的初始化列表阶段显⽰调⽤。

由上图可知在父类我们并没有显示实现默认构造,派生类无法调用父类默认构造,因此必须在派生类初始化列表显示写默认构造,但对于父类成员变量要调用父类的初始化列表进行初始化(person(name)相当于一个匿名对象)

2.拷贝构造

派⽣类(子类)的拷⻉构造函数必须调⽤基类(父类)的拷⻉构造完成基类的拷⻉初始化。

对于该函数,成员变量有一个内置类行变量和一个person(父类)变量,对于自定义变量(person)调用person的拷贝构造即可,但对于内置变量直接进行值拷贝,所以该子类并不需要直接写拷贝构造。

如果有资源开辟那么我们就需要进行自己写子类的拷贝钩爪

3.赋值重载

派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。需要注意的是派⽣类的 operator=隐藏了基类的operator=,所以显⽰调⽤基类的operator=,需要指定基类作⽤域

与拷贝构造分析类似

4.析构

对于派生类调用基类的析构,如果有要释放的资源则需要自己写就行。

自己写的话显示调用父类析构(也可以不写因为子类进行析构后会自动调用父类析构,这样保证先清理子类资源在清理父类资源 ),需要注意的是子类析构和父类析构构成隐藏关系。

5.子类的初始化先调用父类在子

6.析构 先子后父

构造析构图解

实现不能被继承的类

方法一:

c++98规定:只要把构造函数私有化就不能被继承

原因:因为实例化派生类要调用基类的构造函数,如果没有就不能继承

方法二:

在父类后边加一个final说明这个类是最后一个类

class parent final{};

继承与友元

基类的友元关系不能被继承如果想被继承需要在两个类中都加友元

继承与静态成员

普通成员变量定义在基类和派生类则两则是两个不同的变量,静态成员变量如果继承下来则两个类是公用该同一个变量。

多继承及其菱形继承问题

继承类型

单继承:只继承一个父类

多继承:一个子类继承两个父类

菱形继承:多继承继承的两个父类又继承了同一个父类

菱形继承出现的问题:

当实例化菱形继承时存在二议性,和冗余性,冗余性体现在所继承继承继承的两个类代码重复,二义性表现在实例化时不清楚用的时那个类进行实例化的。

虚继承

虚继承可以解决以上问题,在多继承中继承的两个父类加上关键字virtual即可解决。

总结:

现实中尽量不要使用或则设计菱形继承,因为很复杂。

多继承中指针偏移问题?

class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}

多态

概念:简单来说就是多种形态。

分类:编译时多态(静态多态)和运行时多态(动态多态)(重点)

编译时多态

类似于前面学的函数重载和函数模板

以下面例子来讲:

template<class T>
void fun(int,int);
void fun(double,double)

利用函数模板进行传参时,进行不同类型实例化时,函数在编译时就会生成不同的函数。

运行时多态(重点)

一句话概括:不同的对象去完成不同的行为 这样就表现出多态。

多态的构成条件

多态是一个继承关系下的类对象,去调用同一个函数,产生了不同的行为。

虚函数

虚函数:加在类中成员函数前面一个virtual构成虚函数

实现多态的两个必须重要条件

先看以下代码

class person {
public:virtual void Buyticket() {cout << "全价票" << endl;}
};
class student :public person {virtual void Buyticket() {cout << "半价票" << endl;}
};
void fun(person& p) {//必须为父类引用p.Buyticket();//如果不满足多态指向谁就调用谁
}
int main() {person p1;student s1;fun(p1);fun(s1);return 0;
}

1.必须是基类指针或则引用调用虚函数。

2.被调用的的函数必须是虚函数

说明:

要实现多态效果,第一必须是基类的指针或者引用如下:

void fun(person& p) {//必须为父类引用p.Buyticket();
}
int main() {person p1;student s1;fun(p1);fun(s1);return 0;
}

原因:学过继承应该知道只有时基类的指针或则引用才能指向自己或则时子类(对其进行切割)。

第二点就i是派生类必须对基类的虚函数完成重写覆盖,只有重写/覆盖基类和派生类才能有不同的函数,多态的不同形态才能体现出来。

虚函数重写规则:派生类虚函数与基类虚函数返回值相同,函数参数相同,函数名相同)。如下代码即是重写/覆盖。

class person {
public:virtual void Buyticket() {cout << "全价票" << endl;}
};
class student :public person {virtual void Buyticket() {cout << "半价票" << endl;}
};

注意:在重写基类虚函数时,派⽣类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承 后基类的虚函数被继承下来了在派⽣类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样 使⽤,不过在考试选择题中,经常会故意买这个坑,让你 判断是否构成多态(笔试常考)。

加深理解:

多个类构成的多态(只是为了能加深理解)

class animal {
public:virtual void talk() {cout << "0" << endl;};
};
class dog:public animal {
public:virtual void talk() {cout << "汪汪" << endl;}
};
class cat :public animal {
public:virtual void talk() {cout << "喵喵" << endl;}
};
void fun1(animal& a) {a.talk();
}
int main() {animal a;dog d;cat c;fun1(a);fun1(d);fun1(c);return 0;
}

关于多态的一道面试题目

class A
{
public:virtual void func(int val = 1) {cout << "A->" << val << endl; }virtual void test() {func();  //把对象b的指针传给A*//原因是对于继承只是一种形象化的说法,并不会真的把代码拷贝到b对象中,只是存在一种搜索规则,在b类中搜索不到后就去a类中进行搜索。所以这个地方还是A*}
};class B : public A
{
public:void func(int val = 0) {//虽然没加virtual 但与基类构成重写cout << "B->" << val << endl;}
};
int main(int argc, char* argv[])
{B* p = new B;//实例化一个B对象 pp->test();//相当于对象A指针 preturn 0;
}
//A:A->0 B:B->1 C:A->1 D:B->0 E:编译出错 F:以上都不正确---B
//不是D的原因是因为真正的重写是由基类的声明部分加派生类构造部分(重写的本质就是重写虚函数) 所以真正的写法因该是
virtual void func(int val = 1){cout << "B->" << val << endl};//B->1

虚函数重写的⼀些其他问题

协变(了解)

定义:基类虚函数返回基类对象的指针或者引用,派⽣类虚函数返回派⽣类对象的指针或者引⽤时,称为协变

class person {
public:virtual person* Buyticket() {//返回值为personcout << "全价票" << endl;return nullptr;}
};
class student :public person {virtual student* Buyticket() {//返回值为studentcout << "半价票" << endl;return nullptr;}
};
void fun(person& p) {//必须为父类引用p.Buyticket();//如果不满足多态指向谁就调用谁
}
int main() {person p1;student s1;fun(p1);fun(s1);return 0;
}
//运行结果与多态一致 像这种情况称之为协变只需了解即可
析构函数的重写(面试考题:)

问题:为什么基类中的析构 函数建议设计为虚函数

基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键之,都与基类析构函数构成重写,实际上编译器对析构函数的名称构成了重写,编译后的名称统一处理成destructor,因此符合重写条件,所以就构成重写。

c++设计初衷:

class A
{
public:virtual ~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};
// 只有派⽣类Student的析构函数重写了Person的析构函数,下⾯的delete对象调⽤析构函数,才能
//构成多态,才能保证p1和p2指向的对象正确的调⽤析构函数。
int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0;
}

对于以上程序,基类指针既能指向自己也能指向派生类,如果指向派生类(不构成重写的情况下),并且派生类中由资源需要释放,那么就不能去释放派生类中的资源,照成内存泄漏,只有两则构成多泰才能更好的解决这里的问题。

override和final关键字

override,可以帮助⽤⼾检测是否重写。

先看一段程序

class Car {
public:virtual void Dirve(){}
};
class Benz :public Car {
public:virtual void Drive(){ cout << "Benz-舒适" << endl; }
};
int main()
{return 0;
}

 细心的你可能不能发现以上函数并不构成重写(仔细观察Dirve Drive)函数名不同,不构成重写,这时候方便快速检查可以在派生类虚函数后边加一个override既能快速发现错误

class Car {
public:virtual void Dirve(){}
};
class Benz :public Car {
public:virtual void Drive()override{ cout << "Benz-舒适" << endl; }
};
int main()
{return 0;
}

 如果我们不想让 派⽣类重写这个虚函数,那么可以⽤final去修饰

virtual void Drive() final {}//基类的虚函数加final

重载/重写/隐藏三者对比(常考)

虚函数和抽象类

定义:在虚函数的后⾯写上=0,则这个函数为纯虚函数,只要在基类声明即可,无意义。

包含纯虚函数的类叫做抽象类,抽象类不能实例 化出对象,如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。

纯虚函数某种程度上强制了 派⽣类重写虚函数,因为不重写实例化不出对象。

virtual void Drive() = 0;//基类中的虚函数
//基类成为抽象类 强制派生类必须重写

多态的原理

话题引入

虚函数表指针

class Base
{
public:virtual void Func1(){cout<<"Func1()" << endl;}
protected:int _b = 1;char _ch = 'x';
};
int main()
Base b;
cout << sizeof(b) << endl;
return 0;
}
//A.编译报错 B.运⾏报错 C.8 D.12----D

想必大家根据内容对齐的知识都会选择c,但是在该题中存在的virtual函数是一个指针(4个字节)所以一共是12bytes.

⼀个含有虚函数的类中都⾄少都有⼀个虚函数表指针(该指针指向一个指针数组),因为⼀个类所有虚函数的地址要被放到这个类对象的虚函数表中,虚函数表也简称虚表。

多态是如何实现的

问题引入:

从底层的⻆度Func函数中ptr->BuyTicket(),是如何作为ptr指向Person对象调⽤Person::BuyTicket, ptr指向Student对象调⽤Student::BuyTicket的呢?

通过下图我们可以看到,满⾜多态条件后,底层不再是编译时通过调⽤对象确定函数的地址,⽽是运⾏时到指向的对象的虚表中确定对应的虚函数的地址(虚拟函数地址会进行重写)。所以通过这种方式传不同对象就能调用不同的虚函数,完成不同的行为。

虚函数表

注意点:

1.基类对象的虚函数表中存放基类中所有的虚函数地址。

问:为什么一个类中会存放一张虚函数表?

答:为了多个类能够共享一张虚函数表,一定角度节约了资源。

结论:同类型的对象虚表共用,不同对象虚表各自独立。

2.派生类有两部分构成,继承下来的基类和自己的成员变量,继承下来的基类中有虚函数表指针,自己就不在生成虚函数表指针(但这里的虚函数表指针进行的重写/覆盖 ,指针地址也发生了变化)。

3.派生类的虚函数表包含,基类的虚函数地址,派生类重写的虚函数地址,派生类自己的虚函数地址。

4.虚函数的存放地址与普通函数的地址是一样的,编译好后是一段指令,都是存放在代码段的,只是虚函数的地址又存放在虚表中。

5.虚函数表存放在哪里并不确定。

相关文章:

C++ 继承,多态

看前须知&#xff1a; 本篇博客是作者听课时的笔记&#xff0c;不喜勿喷&#xff0c;若有疑问可以评论区一起讨论。 继承 定义&#xff1a; 继承机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有 类特性的基础上进⾏扩展&#xff0c;增…...

Java中的Stream API:从入门到实战

引言 在现代Java开发中&#xff0c;Stream API 是处理集合数据的强大工具。它不仅让代码更加简洁易读&#xff0c;还能通过并行处理提升性能。本文将带你从基础概念入手&#xff0c;逐步深入Stream API的使用&#xff0c;并通过实战案例展示其强大功能。 1. 什么是Stream API…...

QPainter绘制3D 饼状图

先展示图片 核心代码如下&#xff1a; pie.h #ifndef Q3DPIE_H #define Q3DPIE_H#include <QtGui/QPen> #include <QtGui/QBrush>class Pie { public:double value; QBrush brush; QString description; double percentValue;QString p…...

【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (1)

1.问题描述&#xff1a; 客户端创建实况窗后&#xff0c;通过Push kit更新实况窗内容&#xff0c;这个过程是自动更新的还是客户端解析push消息数据后填充数据更新&#xff1f;客户端除了接入Push kit和创建实况窗还需要做什么工作&#xff1f; 解决方案&#xff1a; 通过Pu…...

数据治理与管理

引入 上一篇我们聊了数仓架构设计,它是企业构建数据中台的基石。其本质就是构建一个可靠易用的架构,可以借此将原始数据汇聚、处理,最终转换成可消费使用的数据资源。 在拥有数据资源以后,我们就需要考虑如何利用它,为企业创造价值,让它变成企业的资产而不是负担。也就…...

什么是HTTP/2协议?NGINX如何支持HTTP/2并提升网站性能?

HTTP/2是一种用于在Web浏览器和服务器之间进行通信的协议&#xff0c;旨在提高网站性能和加载速度。它是HTTP/1.1的继任者&#xff0c;引入了许多优化和改进&#xff0c;以适应现代Web应用的需求。HTTP/2的主要目标是减少延迟、提高效率&#xff0c;以及更好地支持并发请求。 …...

安全运维,等保测试常见解决问题。

1. 未配置口令复杂度策略。 # 配置密码安全策略 # vi /etc/pam.d/system-auth # local_users_only 只允许本机用户。 # retry 3 最多重复尝试3次。 # minlen12 最小长度为12个字符。 # dcredit-1 至少需要1个数字字符。 # ucredit-1 至少需要1个大…...

jmeter接口测试(二)

一、不同参数类型的接口测试 二、动态参数接口处理 随机数 工具——>函数助手对话框&#xff08;Random 1000-10000之间的随机数 变量名为rdn&#xff09;如下图所示 把上图生成的函数字符串复制到想要使用的地方如下图 三、断言 1、状态断言&#xff0c;200 不能证明…...

Keil ARM Complier Missing Compiler Version 5

使用Keil软件时出现了编译时报错,找不到对应的ARM版本,报错Target Target 1 uses ARM-Compiler Default Compiler Version 5 which is not available. *** Please review the installed ARM Compiler Versions: Manage Project Items - Folders/Extensions to manage ARM Compi…...

【僵尸进程】

【僵尸进程】 目录&#xff1a;知识点1. 僵尸进程的定义2. 僵尸进程产生的原因3. 僵尸进程的危害4. 如何避免僵尸进程 代码示例产生僵尸进程的代码示例避免僵尸进程的代码示例&#xff08;父进程主动回收&#xff09;避免僵尸进程的代码示例&#xff08;信号处理&#xff09; 运…...

【框架】参考 Spring Security 安全框架设计出,轻量化高可扩展的身份认证与授权架构

关键字&#xff1a;AOP、JWT、自定义注解、责任链模式 一、Spring Security Spring Security 想必大家并不陌生&#xff0c;是 Spring 家族里的一个安全框架&#xff0c;特别完善&#xff0c;但学习成本比较大&#xff0c;不少开发者都觉得&#xff0c;这个框架“很重” 他的…...

【Git 学习笔记_27】DIY 实战篇:利用 DeepSeek 实现 GitHub 的 GPG 密钥创建与配置

文章目录 1 前言2 准备工作3 具体配置过程3.1. 本地生成 GPG 密钥3.2. 导出 GPG 密钥3.3. 将密钥配置到 Git 中3.4. 测试提交 4 问题排查记录5 小结与复盘 1 前言 昨天在更新我的第二个 Vim 专栏《Mastering Vim (2nd Ed.)》时遇到一个经典的 Git 操作问题&#xff1a;如何在 …...

微信小程序地图map全方位解析

微信小程序地图map全方位解析 微信小程序的 <map> 组件是一个功能强大的工具&#xff0c;可以实现地图展示、定位、标注、路径规划等多种功能。以下是全方位解析微信小程序地图组件的知识点&#xff1a; 一、地图组件基础 1. 引入 <map> 组件 在页面的 .wxml 文…...

调试无痛入手

在调试过程中&#xff0c;Step In、Step Over 和 Step Out 是控制代码执行流程的常用操作&#xff0c;帮助开发者逐行或逐块检查代码行为。以下是它们的详细介绍及使用方法&#xff1a; 1. Step In 功能&#xff1a;进入当前行的函数或方法内部&#xff0c;逐行执行其代码。使…...

【蓝桥杯集训·每日一题2025】 AcWing 6135. 奶牛体检 python

6135. 奶牛体检 Week 1 2月21日 农夫约翰的 N N N 头奶牛站成一行&#xff0c;奶牛 1 1 1 在队伍的最前面&#xff0c;奶牛 N N N 在队伍的最后面。 农夫约翰的奶牛也有许多不同的品种。 他用从 1 1 1 到 N N N 的整数来表示每一品种。 队伍从前到后第 i i i 头奶牛的…...

AI发展迅速,是否还有学习前端的必要性?

今天有个小伙伴跟我讨论&#xff1a;“现在 AI 发展迅速&#xff0c;是否还有学习 JS 或者 TS 及前端知识的必要&#xff1f;” 我非常肯定地说&#xff1a; 是的&#xff0c;学习 JavaScript/TypeScript 以及前端知识仍然非常必要&#xff0c;而且在可预见的未来&#xff0c;…...

【数据标准】数据标准化是数据治理的基础

导读&#xff1a;数据标准化是数据治理的基石&#xff0c;它通过统一数据格式、编码、命名与语义等&#xff0c;全方位提升数据质量&#xff0c;确保准确性、完整性与一致性&#xff0c;从源头上杜绝错误与冲突。这不仅打破部门及系统间的数据壁垒&#xff0c;极大促进数据共享…...

VS2022配置FFMPEG库基础教程

1 简介 1.1 起源与发展历程 FFmpeg诞生于2000年&#xff0c;由法国工程师Fabrice Bellard主导开发&#xff0c;其名称源自"Fast Forward MPEG"&#xff0c;初期定位为多媒体编解码工具。2004年后由Michael Niedermayer接任维护&#xff0c;逐步发展成为包含音视频采…...

three.js之特殊材质效果

*案例42 创建一个透明的立方体 <template><div ref"container" className"container"></div> </template><script setup> import * as THREE from three; import WebGL from three/examples/jsm/capabilities/WebGL.js // 引…...

Qt常用控件之日历QCalendarWidget

日历QCalendarWidget QCalendarWidget 是一个日历控件。 QCalendarWidget属性 属性说明selectDate当前选中日期。minimumDate最小日期。maximumDate最大日期。firstDayOfWeek设置每周的第一天是周几&#xff08;影响日历的第一列是周几&#xff09;。gridVisible是否显示日历…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...