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

继承(c++版 非常详细版)

        一. 继承的概念及定义

        

1.1 继承的概念

继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有 类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承 呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的 复⽤,继承是类设计层次的复⽤。

下⾯我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity⾝份认证的成员函数,设计到两个类⾥⾯就是冗余的。当然他们 也有⼀些不同的成员变量和函数,⽐如⽼师独有成员变量是职称,学⽣的独有成员变量是学号;学⽣ 的独有成员函数是学习,⽼师的独有成员函数是授课。

        直接上一个例子看看。

        

        这两个类是有很多相同的属性的,我们能不能把相同的只放在一个类中减少冗余呢?

        这时候继承就可以完美解决这个问题了。

        

        此时我们就把老师和同学相同的属性全部都抽出来了,放在了一个Person类中,这里你可以先看看继承的格式是怎么写的。

1.2 继承定义

        

下⾯我们看到Person是基类,也称作⽗类。Student是派⽣类,也称作⼦类。(因为翻译的原因,所以既叫基类/派⽣类,也叫⽗类/⼦类)。

        

        继承方式和访问限定符都是有三类的。

        

        这个就是继承的方式,访问限定符会缩小,但是不会变大。我们下面来看一下。理解一下。

        我们来举个例子就很清楚了。

        

        此时这个学生类是通过public继承的,所以父类中各个访问限定符就不会改变,还是原来的,不变。

        但是我们换个方式继承看看。

        

        我们通过protected访问限定符来继承,此时创建子类对象访问父类中的方法时,这里的父类的public访问限定符就会变成protected,父类对象不受影响,我们可以验证一下。

        

        我们看到没,子类对象此时访问不到父类中的identity方法了,但是父类对象可以。

        我们再改为private继承试一下。

        

        此时子类对象访问父类方法时,父类中的方法全部变为private类型的了。

        此时派生类中还是按照原来的访问,只是子类对象的访问限定符改变了。

        大家可以好好看看这个例子就可以理解这两种情况了。

        

1. 基类private成员在派⽣类中⽆论以什么⽅式继承都是不可⻅的。这⾥的不可⻅是指基类的私有成员 还是被继承到了派⽣类对象中,但是语法上限制派⽣类对象不管在类⾥⾯还是类外⾯都不能去访问它。

        

        这句话的意思就是我改为私有的,此时除了在自己类中可以访问的到,在其他类中都是访问不到的,派生类中也是访问不到的。

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

3. 实际上⾯的表格我们进⾏⼀下总结会发现,基类的私有成员在派⽣类都是不可⻅。基类的其他成员 在派⽣类的访问⽅式 == Min(成员在基类的访问限定符,继承⽅式),public > protected >

private。

4. 使⽤关键字class时默认的继承⽅式是private,使⽤struct时默认的继承⽅式是public,不过最好显 ⽰的写出继承⽅式。

5. 在实际运⽤中⼀般使⽤都是public继承,⼏乎很少使⽤protetced/private继承,也不提倡使⽤

protetced/private继承,因为protetced/private继承下来的成员都只能在派⽣类的类⾥⾯使⽤,实

际中扩展维护性不强。

1.3 继承类模板

        这个我们就直接上代码来理解吧。

        

        这个就是我们常用的栈,我们在这的实现是通过让它继承vector容器来实现的,此时我们是可以继承vector中的方法的。

        上面的代码可能会有些疑惑的地方,比如,为什么要在方法前面加上vector<T>呢?

        最主要有两个方面的作用。

        1.避免二义性:当模板类stack继承自std::vector<T>时,stack类中可能会有与父类std::vector<T>成员函数同名的成员函数或者其他成员。如果不使用vector<T>::限定,编译器可能无法确定你要调用的是父类的成员函数还是子类中可能存在的同名成员,加上作用域限定符可以明确指定要调用的是父类std::vector<T>中的成员函数,避免产生二义性。

        2.模板的延迟实例化:模板是按需实例化的。在stack类模板中,当实例化stack<int>时,虽然也会实例化vector<int>,但vector<int>中的成员函数如push_back、pop_back等可能并没有立即被实例化。如果直接使用push_back等函数而不加vector<T>::限定,编译器在编译stack类的成员函数时可能无法找到这些函数的定义,因为它们可能还没有被实例化。而加上vector<T>::限定,编译器就能够明确知道要在父类std::vector<T>的作用域中查找这些函数,即使它们还没有被实例化,也能正确地进行编译,在需要的时候再进行实例化。

        简单来说就是,你的vector<T>中的T虽然被实例化了,但是vector这个类中的方法可能没有被实例化,你直接使用可能编译器不知道使用哪个push_back方法,因为我们都知道模板的底层还是多个类型的函数吗,只是编译器帮我们写了,我们不需要自己写了,所以我们不加前缀,他不知道调用哪个类型的。

        

        看此时我们不仅可以调用自己的方法,还可以调用基类中的方法,效果都是一样的。

        

        

        你看如果我们直接说明了T的类型,此时就不用加前缀了,此时确定了是int,它的方法就直接会实例化了。

1.4 基类和派⽣类间的转换

       

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

割。寓意把派⽣类中基类那部分切出来,基类指针或引⽤指向的是派⽣类中切出来的基类那部分

基类对象不能赋值给派⽣类对象。

举个例子吧。

        我们由浅入深来看一下吧。

        

        我们看一下这个代码,前两行的int引用引用int变量是没有问题的,我们发现double引用引用int类型是不行的,这是因为类型转换会产生临时变量,引用无法指向临时变量,必须加const延长临时对象的声明周期。

        

        这样就没问题了。

        我们再来看看继承中的类型转换。

        

        这时候为什么没有报错呢?不是需要类型转换吗?生成临时变量为什么可以呢?

        首先第一个,我们用普通类型是无法实现的,为什么我们的继承可以呢?还有第二个,我们不是需要加const吗,为什么这里不用加呢?

        答案是不会的,你可以把它理解为切割,因为父类中的成员子类都可以访问(这个例子中的),所以当用指针或者引用是,它会只切割自己要的部分,不会产生临时变量,基类指针或者引用能够指向派生类对象。这是因为 public 继承维持了基类接口的公共性,派生类对象可被视为基类对象,所以这种转换是安全的。

        

        此时就把派生类对象看成一个基类对象了。

        我们用protected和private继承是不能实现的。

        基类指针或者引用不可以直接指向派生类对象。private 继承意味着派生类把基类的接口变成了自己的私有接口,外部无法将派生类对象当作基类对象来处理。

        你可以理解为外部对象无法访问到基类中的方法了,此时不能将一个派生类看成一个基类了。

        

1.5  继承中的作⽤域

        

1.5.1 隐藏规则:

1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。

2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。

(在派⽣类成员函数中,可以使⽤ 基类::基类成员 显⽰访问)

        

        举个简单的例子看一下吧。

        可以看A中的func函数,如果直接访问id,默认访问的是自己的,因为隐藏规则吗,此时B的id就把A的id给覆盖了,想访问A的可以用下面的那种方法,指定作用域即可。

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

        这个我们举个例子,印象能更深刻,我们直接看两个题。

        

class A

{

public:

void fun()

{

cout << "func()" << endl;

}

};

class B : public A

{

public:

void fun(int i)

{

cout << "func(int i)" <<i<<endl;

}

};

int main()

{

B b;

b.fun(10);

b.fun();

return 0;

};

        结合上面的代码看两个题吧。

        

        如果没有上面那句话的提醒,可能好多人都会选A     C了,但是结合上面的话我们可以知道,只要名字相同就是隐藏和参数没关系,他不会构成重载,而是隐藏,此时也会编译报错,A类的方法被隐藏了,你调用的func函数都是B类的,你没有传参数,此时就会编译报错了。

        

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

1.6 派⽣类的默认成员函数

        1.6.1 4个常⻅默认成员函数

        

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

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

        我们举个例子。

        

        

        

        我们发现这样是没问题的,它的过程就是通过自己的默认构造去调用Person的默认构造,因为Person是有缺省值的构造,你不传参数也是可以的,可以认为就是默认构造。

        

        如果我们没给默认构造呢?

        此时就会报错了,因为派生类的默认构造在调用基类的默认构造的时候,发现找不到基类的默认构造,此时就报错了,此时我们该怎么办呢?

        只需要自己实现一下派生类的构造就行了。

        需要注意一点:子类中父类成员变量当成整体对象,作为子类自定义类型成员看待

        

        此时如果我们像之前那样初始化,此时就又错了,我们不能这样初始化,而是应该把基类的所有变量看成一个对象来初始化。

        

        这样就没问题了,相当于你需要调用基类的构造先初始化基类,否则就会报错了。

        此时发现没问题了。

2. 派⽣类的拷⻉构造函数必须调⽤基类的拷⻉构造完成基类的拷⻉初始化。

        我们还是看上图的最后一个的拷贝构造,它会调用父类的拷贝构造,id再调用自己的默认拷贝构造,因为这里我们的成员变量不需要深拷贝,此时就不用写子类的拷贝构造了,除非需要深拷贝。

        

        我们给子类加了一个指针变量,此时就需要深拷贝了,我们来写一下子类的拷贝构造吧。

        此时还会面临一个问题,我们和构造一样需要调用父类的拷贝构造,需要传入一个父类对象,但是我们此时没有父类的对象呀,此时该怎么办呢?

        

        我们通过上面我们讲的转换原则,直接传入一个派生类对象即可,它会自己切割得到自己想要的基类对象。

3. 派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。需要注意的是派⽣类的

operator=隐藏了基类的operator=,所以显⽰调⽤基类的operator=,需要指定基类作⽤域

        

        这是我们子类写的=运算符我们先来运行一下。

        

        我们发现崩溃了,这是为什么呢?

        我们通过调试发现,栈溢出了,这是因为,又是构成隐藏了,一直调用的是自己的operator=,自己调用自己无法结束,就导致栈溢出了。

        

        只需要像这样指定一下类域即可。

4. 派⽣类的析构函数会在被调⽤完成后⾃动调⽤基类的析构函数清理基类成员。因为这样才能保证派⽣类对象先清理派⽣类成员再清理基类成员的顺序。

        

        这里我们写了子类的析构去调用父类的析构为什么报错了呢?

        因为调用父类的析构需要指定类域。

        

        

     为什么调用了四次析构呢?理论应该两次啊,其实这里就是派生类的析构会自动调用基类的析构,又显示调用了一下,此时就会一个对象调用两次,我们两个对象,所以调用了四次。

5. 派⽣类对象初始化先调⽤基类构造再调派⽣类构造。

        这个你可以通过调试看看,一直都是先走父类的构造初始化。

6. 派⽣类对象析构清理先调⽤派⽣类析构再调基类的析构。

        所以我们不需要写显示调用父类的析构了,这样也可以保证析构的时候先子后父,因为构造的时候是先父后子,先创建的后析构吗。

7. 因为多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同(这个我们多态章节会讲解)。那么编译器会对析构函数名进⾏特殊处理,处理成destructor(),所以基类析构函数不加

virtual的情况下,派⽣类析构函数和基类析构函数构成隐藏关系。

        1.7 实现⼀个不能被继承的类

        一共有两种做法。

        一.c++98实现的

        

        我们可以看一下,就是利用了一个语法漏洞,把父类的友元函数弄成私有的,此时调用不到,就会无法实例化对象,但是也有缺陷,此时A也无法使用默认构造了。

        二.c++11完成的

        

        使用final关键字,此时就不能被继承,但是不影响A的默认构造。

1.8 继承与友元

        

        这里报了一个错误,大家不要被编译器的报错迷惑,其实不是这里的错误,是上面的原因,因为编译器为了提升编译速度只会向上找,此时找不到B,此时这里就会有问题,我们只需要在上面给上B的声明即可。

        

        此时我们可以看到,父类的友元函数是父类的,但是子类没有继承过来,不能继承,简单来说就是你爸爸的朋友不是你的朋友。

        

        1.9 继承与静态成员

        静态成员是可以继承的。

        

        基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都只有⼀个static成员实例。
        这个也是很好理解的就是所有实例化出来的对象访问的都是同一个静态成员。
        
我们可以通过这个来看一下。
我们发现普通变量都是独立的,每个对象都是不同的,从地址我们也可以看出来,但是这个静态成员变量是唯一的,多个对象访问的都是同一个静态成员变量,静态成员是可以直接通过类名来访问的不需要实例化对象就可以访问。

1.10 多继承及其菱形继承问题

        

1.10.1 继承模型

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

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

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以 看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就 ⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

        

        这就是菱形继承,二义性就是,我们的Student和Teacher都继承了Person的_name变量,导致,我们实例化Assistance的时候,访问_name此时就会出错了,不知道访问的是Student的还是Teacher的,此时就出现了二义性,就需要指定类域的方式解决了。

        这就是个菱形继承,此时出现了二义性。

        解决方法:

        

        指定类域即可,但是数据冗余我们无法解决,我们可以看到,Teacher继承的_name我们现在用不到,没啥用,我们只用一个就行了,冗余了一个。

        怎么解决呢?

        

1.10.2 虚继承

        很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。

        这个就是把两个对象的冗余合并为了一个。

        我们知道是Student和Teacher都继承了Person导致的,只需要在这两个类的继承前面加上virtual即可。

        

        就是如图所示。

        

        我们也可以通过调试看出来,确实此时Teacher和Student的_name都是同一个_name了,这只限于自己的派生类继承时这样,创建Student和Teacher对象的时候还是都是独立的_name。

        

        此时创建的这两个对象的变量还是独立的值,只会影响下面的派生类的菱形继承。

        我们再举个例子理解一下菱形继承。

        

        这显然也是一个菱形继承了,二义性和数据冗余是由于B,C引起的,所以需要在B,C的后面加上virtual。

        

        这是底层的实现,我们就不写代码了,这个不是特别重要,实践中用的也不多。

        

        我们下面来看一道题吧。

        

        答案是C,下面我们来讲解一下。

        

内存布局

在多继承的情况下,派生类对象的内存布局是按照基类在派生类定义中出现的顺序依次排列的,接着是派生类自身的成员。以 Derive 类为例,它先包含 Base1 的成员,然后是 Base2 的成员,最后是 Derive 自身的成员。内存布局如下:

        

        

指针偏移原因

  • Base1* p1 = &d;:由于 Base1 是 Derive 继承的第一个基类,p1 指向的地址与 d 对象的起始地址相同,所以 p1 不会发生偏移。
  • Base2* p2 = &d;Base2 是 Derive 继承的第二个基类,Base2 成员在内存中是紧跟在 Base1 成员之后的。为了让 p2 正确指向 Base2 部分的内存,编译器会对 p2 进行偏移操作,使其指向 Base2 成员的起始地址。偏移量就是 Base1 类对象所占的内存大小。
  • Derive* p3 = &d;p3 是 Derive 类型的指针,它指向 d 对象的起始地址,所以 p3 不会发生偏移。

        这就是偏移的原因的,简单来说就是基类的指针要指向自己部分内存的首地址。

1.11  继承和组合

        

public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。

组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。

        我们来举个组合的例子吧。

        

        这是一个组合关系,stack中有一个vector容器,是has-a的关系,它俩的关系就是组合了,不是继承,继承是is-a的关系,stack不是vector。

继承允许你根据基类的实现来定义派⽣类的实现。这种通过⽣成派⽣类的复⽤通常被称为⽩箱复⽤ (white-box reuse)。术语“⽩箱”是相对可视性⽽⾔:在继承⽅式中,基类的内部细节对派⽣类可 ⻅ 。继承⼀定程度破坏了基类的封装,基类的改变,对派⽣类有很⼤的影响。派⽣类和基类间的依赖关系很强,耦合度⾼。

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

优先使⽤组合,⽽不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好。不过也不太 那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的关系既适合⽤继承(is-a)也适合组合(has-a),就⽤组合。

        下面再来讲一下耦合度吧,好好理解一下。

        耦合度就是关联关系,我们在项目中肯定是耦合度越低越好,这样你改变一个类,影响的东西就少了。

        举个例子就是,一家人出去旅游,如果一块去坐同一辆车,可能这个有个事情,那个有个事情,可能就会耽误,但是如果是两人一辆车,分多辆车去的话,这样,如果两个人有事来不了,但是这个旅游活动还是可以进行的,影响不到其他人的旅游。

二.结束语

        感谢大家的查看,希望可以帮助到大家,做的不是太好还请见谅,其中有什么不懂的可以留言询问,我都会一一回答。  感谢大家的一键三连。

相关文章:

继承(c++版 非常详细版)

一. 继承的概念及定义 1.1 继承的概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有 类特性的基础上进⾏扩展&#xff0c;增加⽅法(成员函数)和属性(成员变量)&#xff0c;这样产⽣新的类&#xff0c;称派⽣类。继…...

解锁服务器迁移的未来:《2025 服务器迁移效率白皮书》(附下载)

一、背景&#x1f3d9;️ 随着全球数字化转型的不断加速&#xff0c;企业在推动 IT 基础设施现代化过程中&#xff0c;面临着前所未有的服务器迁移挑战。传统的迁移工具和多云、混合云环境带来的复杂性&#xff0c;导致迁移效率低、成本高、人力投入大&#xff0c;从而严重阻碍…...

STM32的Flash映射双重机制

在STM32微控制器中&#xff0c;存在一个重要的内存映射特性&#xff1a;Flash存储器可以同时出现在两个不同的地址区域&#xff0c;而且可以通过重映射功能改变CPU启动时从哪个地址获取初始指令。 STM32的Flash映射双重机制 当描述"通常起始于地址0x00000000&#xff0c…...

简单了解跨域问题

什么是跨域&#xff1f; 跨域是浏览器基于同源策略的安全机制。 如何两个请求之间&#xff0c;域名&#xff0c;端口&#xff0c;协议三者中有任意一个不同&#xff0c;就会产生跨域问题。 跨域的解决方案 1. CORS&#xff08;跨源资源共享&#xff09; 后端通过设置响应头声…...

sql学习笔记(四)

今天看到一个sql题&#xff0c;“近30天&#xff0c;******”&#xff0c;这里需要用到一个函数&#xff0c;date_add&#xff0c;其作用是在指定日期基础上添加一个时间间隔。 语法&#xff08;以mysql为例&#xff09;&#xff1a; DATE_ADD(date, INTERVAL value unit) d…...

基于 Java 的实现前端组装查询语句,后端直接执行查询方案,涵盖前端和后端的设计思路

1. 前端设计 前端负责根据用户输入或交互条件,动态生成查询参数,并通过 HTTP 请求发送到后端。 前端逻辑: 提供用户界面(如表单、筛选器等),让用户选择查询条件。将用户选择的条件组装成 JSON 格式的查询参数。发送 HTTP 请求(如 POST 或 GET)到后端。示例: 假设用…...

反射与注解实现动态功能扩展案例-插件系统

学海无涯&#xff0c;志当存远。燃心砺志&#xff0c;奋进不辍。 愿诸君得此鸡汤&#xff0c;如沐春风&#xff0c;事业有成。 若觉此言甚善&#xff0c;烦请赐赞一枚&#xff0c;共励学途&#xff0c;同铸辉煌&#xff01; 开发一个需要高度扩展性的应用&#xff0c;比如Web框…...

auto(x) decay copy

该提案为auto又增加了两个新语法&#xff1a;auto(x) 和auto{x}。两个作用一样&#xff0c;只是写法不同&#xff0c;都 是为x 创建一份拷贝。 为什么需要这么个东西&#xff1f;看一个例子&#xff1a; void bar(const auto&);void foo(const auto& param) {auto co…...

基于STM32、HAL库的DS2411R安全验证及加密芯片驱动程序设计

一、简介: DS2411R是Maxim Integrated(现为Analog Devices)生产的一款1-Wire硅序列号芯片,具有以下特点: 64位唯一ROM序列号(包括8位家族码、48位序列号和8位CRC校验码) 工作电压范围:2.8V至5.25V 工作温度范围:-40C至+85C 采用TO-92或SOT-223封装 通过1-Wire协议通信…...

疫苗接种体系进入“全生命周期”时代:公共卫生治理再提速

疫苗接种体系进入“全生命周期”时代&#xff1a;公共卫生治理再提速 在防控重大传染病的国家公共卫生战略中&#xff0c;疫苗接种始终处于基础性、先导性地位。2025年4月25日是第39个全国儿童预防接种日&#xff0c;活动主题为“打疫苗、防疾病、保健康”。近年来&#xff0c…...

zynq 7010 PS 串口打印

前言 之前写过一篇文章《zynq 7010 PL 点灯例程》&#xff0c;介绍的是 zynq PL 部分的使用&#xff0c;今天这篇文章则是介绍 zynq PS 部分的使用。 在此之前&#xff0c;先总结点题外话 PL 编程&#xff0c;核心思想是生成 bitstream 文件&#xff0c;加载到 FPGA 运行PS …...

【补题】ACPC Kickoff 2025 F. Kinan The Bank Robber

题意&#xff1a;给出长度为n的序列&#xff0c;接下来给出了两个包裹&#xff0c;你可以选择把数字放进这两个包裹当中&#xff0c;要求你放的的方式&#xff0c;最终会让包裹内的数字双双互质&#xff0c;请你给出你的放法&#xff0c;如果没有输出-1 思路&#xff1a; 1.包…...

局域网传文件——基于flask实现

项目地址 git clone gitgitee.com:xhdx/co_-shared_-doc_in_-local_-net.git 所需python包 flask2.2.3 markdown3.4.1 bleach5.0.1 通过局域网的方式实现文件夹共享&#xff0c;共享的文件会放在uploads这个文件夹下&#xff1a; 运行界面&#xff1a; 包括预览、删除、下载等…...

苍穹外卖10

WebSocket WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信----浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输。 HTTP协议和WebSocket协议对比&#xff1a; HTTP是短链接 WebSocke…...

第一天 车联网定义、发展历程与生态体系

前言 车联网&#xff08;Internet of Vehicles, IoV&#xff09;作为物联网&#xff08;IoT&#xff09;在汽车领域的延伸&#xff0c;正在彻底改变人们的出行方式。无论是自动驾驶、远程诊断&#xff0c;还是实时交通优化&#xff0c;车联网技术都扮演着核心角色。本文将从零…...

大模型(LLMs)强化学习—— PPO

一、大语言模型RLHF中的PPO主要分哪些步骤&#xff1f; 二、举例描述一下 大语言模型的RLHF&#xff1f; 三、大语言模型RLHF 采样篇 什么是 PPO 中 采样过程&#xff1f;介绍一下 PPO 中 采样策略&#xff1f;PPO 中 采样策略中&#xff0c;如何评估“收益”&#xff1f; …...

麻衣相法【麻衣相士】开篇

好久没有发布新的文章了,主要最近一方面没看书,时间基本都用来打游戏了;另一方面看的几本书实在是看不懂,就更不能写上来了。不过今天看到了《麻衣相法》这本书,就又点燃了本人的兴趣,以后失业了,可以摆个小摊子谋生! 话不多说,先把这本书开始的针对人的面部部位进行各…...

vLLM技术解析:大语言模型推理服务的性能革新引擎

vLLM大模型 vLLM&#xff08;Vectorized Large Language Model Serving System&#xff09;是由加州大学伯克利分校计算机系统研究团队开发的下一代大语言模型推理服务系统。作为专为现代化AI部署设计的开源框架&#xff0c;该系统通过突破性的内存架构创新和计算流程优化&…...

《无刷空心杯电机减速机选型及行业发展趋势》

无刷空心杯电机作为高精度驱动系统的核心部件,通常需搭配减速机以实现低转速、高扭矩输出。以下是当前主流的减速机类型、市场占比、参数对比及优劣势分析,结合行业数据与典型应用场景展开说明: 一、主流减速机类型及市场占比 1. 行星减速机 市场占比:约 45%-55%(工业自…...

Java锁的升级流程详解:无锁、偏向锁、轻量级锁、重量级锁

在Java中&#xff0c;为了在多线程并发场景下既保证线程安全&#xff0c;又尽可能提高性能&#xff0c;JVM针对synchronized实现了锁的优化升级机制。 锁可以从无锁逐步升级到偏向锁、轻量级锁&#xff0c;最后是重量级锁。 话不多说&#xff0c;发车&#xff01; 一、无锁&am…...

terraform local-exec与remote-exec详解

在 Terraform 中&#xff0c;local-exec 和 remote-exec 是两种常用的 provisioner&#xff08;资源调配器&#xff09;&#xff0c;用于在资源创建前后执行脚本或命令。它们的核心区别在于执行位置&#xff1a;local-exec 在运行 Terraform 的本地机器上执行命令&#xff0c;而…...

武装Burp Suite工具:APIKit插件_接口安全扫描.

武装Burp Suite工具&#xff1a;APIKit插件_接口安全扫描. API安全是指通过技术手段和管理措施保护应用程序接口&#xff08;API&#xff09;免受未授权访问、数据泄露或恶意攻击的防护体系&#xff0c;核心措施包括身份认证&#xff08;如OAuth2.0/JWT&#xff09;、权限控制…...

数据库系统概论|第三章:关系数据库标准语言SQL—课程笔记6

前言 经过前面几篇文章的介绍&#xff0c;已经完成了对于数据查询操作的介绍&#xff0c;接下来&#xff0c;本篇文章将介绍数据更新这一板块&#xff0c;包括插入数据、修改数据以及删除数据三种操作方法。 注&#xff1a;本文中所涉及的数据库前文中已经介绍&#xff08;指…...

如何在idea中写spark程序

1. 安装配置 Java 和 Scala Java&#xff1a;确保已安装合适版本的 Java Development Kit&#xff08;JDK&#xff09;&#xff0c;并配置好 JAVA_HOME 环境变量。Scala&#xff1a;由于 Spark 常用 Scala 语言编写&#xff0c;需安装 Scala 开发环境。可在 IDEA 中通过 Se…...

Linux428 chmod 0xxx 1xxx 2xxx 4xxx;umask;chown 属主属组 软件包rpm

sudo: 账户过期&#xff0c;或 PAM 配置缺少 sudo 使用的“account”节&#xff0c;联系您的系统管理员 这样子有没有用嘞 不行 为什么使用caozx26用户sudo修改了shop文件夹强制位&#xff0c;shop文件夹权限中不显示 成功了&#xff1f;真奇怪 查看文件夹权限用 -ld ch…...

基于强化学习的用于非刚性图像配准的引导式超声采集|文献速递-深度学习医疗AI最新文献

Title 题目 Guided ultrasound acquisition for nonrigid image registration usingreinforcement learning 基于强化学习的用于非刚性图像配准的引导式超声采集 01 文献速递介绍 超声成像通常用于引导手术和其他医疗程序&#xff0c;在这些过程中&#xff0c;临床医生会持…...

Shell脚本-嵌套循环应用案例

在Shell脚本编程中&#xff0c;嵌套循环是一种强大的工具&#xff0c;可以用于处理复杂的任务和数据结构。通过在一个循环内部再嵌套另一个循环&#xff0c;我们可以实现对多维数组、矩阵操作、文件处理等多种高级功能。本文将通过几个实际的应用案例来展示如何使用嵌套循环解决…...

QTableView复选框居中

目录 方法一&#xff1a;QSS方法2:自定义复选框委托类一、构造函数 CheckBoxDelegate()二、paint() 方法三、editorEvent() 方法四、关键设计要点五、扩展应用场景六、代码示例&#xff08;补充&#xff09; 方法一&#xff1a;QSS QTableView::indicator {position: relative…...

C语言教程(十八):C 语言共用体详解

一、共用体的定义 共用体的定义和结构体类似&#xff0c;使用 union 关键字&#xff0c;其基本语法如下&#xff1a; union 共用体名 { 数据类型 成员1; 数据类型 成员2; // 可以有更多成员 }; 以下是一个简单的共用体定义示例&#xff1a; union Data {int i;float f;char …...

企业办公系统开发如何重塑现代工作方式?

随着工作方式的革新&#xff0c;企业办公软件开发已成为提升组织效率的核心驱动力。从基础的文档处理到复杂的协同办公平台开发&#xff0c;现代办公系统正在彻底改变传统工作模式。本文将深入解析办公类软件的关键技术与发展趋势。 一、企业级办公系统开发的核心模块 专业的O…...

springboot 实现敏感信息脱敏

记录于2025年4月28号晚上--梧州少帅 1. 定义枚举类&#xff1a; public enum DesensitizeType {NAME, EMAIL } 2. 创建自定义注解&#xff1a; 用于标记需要脱敏的字段及其类型。 Retention(RetentionPolicy.RUNTIME) JacksonAnnotationsInside JsonSerialize(using Desen…...

JavaWeb学习打卡-Day5-Spring事务管理、SpringAOP

Spring事务管理 Transactional注解 位置&#xff1a;业务层&#xff08;Service&#xff09;的方法上、类上、接口上。作用&#xff1a;将当前方法交给spring进行事务管理&#xff0c;方法执行前&#xff0c;开启事务&#xff1b;成功执行完毕&#xff0c;提交事务&#xff1…...

项目立项管理

项目立项管理是对拟规划和实施的项目技术上的先进性、适用性&#xff0c;经济上的合理性、效益性&#xff0c;实施上的可能性、风险性以及社会价值的有效性、可持续性等进行全面科学的综合分析&#xff0c;为项目决策提供客观依据的一种技术经济研究活动。 一般包括项目建议与…...

一文梳理业财融合在财务管理中的运用!

目录 一、业财融合在财务管理中的运用概括 二、促进财务决策科学化 1. 传统财务决策的局限性 2. 业财融合助力财务决策科学化 三、加强成本控制 1. 传统成本控制的不足 2. 业财融合实现精准成本管控 四、优化资金管理 1. 传统资金管理的问题 2. 业财融合优化资金配置…...

C#核心知识

委托 如何声明一个委托&#xff1a;通过 【delegate 返回值类型 委托名称】 的格式来定义 如何使用一个委托&#xff1a;使用new关键字&#xff0c;并传入和声明委托的构造相同的方法名&#xff0c;比如&#xff1a;new 委托名称(与委托的参数和返回值相同的一个方法名) 如何…...

[多彩数据结构] 笛卡尔树

[多彩数据结构] 笛卡尔树 定义 笛卡尔树&#xff0c;就是一棵树&#xff08;废话&#xff09;中存两个信息&#xff0c;为 ( w , i ) (w,i) (w,i)。其中 k w e i g h t , i i d kweight,iid kweight,iid。 即 w w w 存的是节点的值&#xff0c; i i i 存的是编号。 每…...

【Spark入门】Spark RDD基础:转换与动作操作深度解析

目录 1 RDD编程模型概述 1.1 RDD操作分类 2 常用转换操作详解 2.1 基本转换操作 2.2 键值对转换操作 2.3 复杂转换操作 3 动作操作触发机制 3.1 常见动作操作 3.2 动作操作性能对比 4 RDD执行机制深度解析 4.1 惰性求值原理 4.2 任务生成过程 5 性能优化实践 5.1 …...

一文了解 模型上下文协议(MCP)

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;是由Anthropic公司于2024年11月推出的一项开放标准协议&#xff0c;旨在解决大型语言模型&#xff08;LLM&#xff09;与外部数据源和工具之间的通信问题。其核心目标是通过提供一个标准化的接口&…...

每日算法-250428

每日算法 - 2024年4月28日 记录今天完成的几道 LeetCode 算法题。 1877. 数组中最大数对和的最小值 题目描述: 思路 贪心策略。 解题过程 为了最小化所有数对和中的最大值&#xff0c;直观的想法是避免让两个较大的数相加。因此&#xff0c;最优策略是将数组中最小的元素…...

【“星瑞” O6 评测】 — CPU llama.cpp不同优化速度对比

前言 随着大模型应用场景的不断拓展&#xff0c;arm cpu 凭借其独特优势在大模型推理领域的重要性日益凸显。它在性能、功耗、架构适配等多方面发挥关键作用&#xff0c;推动大模型在不同场景落地 1. Kleidi AI 简介 Arm Kleidi 成为解决这些挑战的理想方案&#xff0c;它能…...

Redis 常见问题深度剖析与全方位解决方案指南

Redis 是一款广泛使用的开源内存数据库&#xff0c;在实际应用中常会遇到以下一些常见问题&#xff1a; 1.内存占用问题 问题描述&#xff1a;随着数据量的不断增加&#xff0c;Redis 占用的内存可能会超出预期&#xff0c;导致服务器内存不足&#xff0c;影响系统的稳定性和…...

在g2o图优化框架中,顶点(Vertex)和边(Edge)的定义与功能的区别

在g2o图优化框架中,顶点(Vertex)和边(Edge)是构建优化问题的核心组件,两者的定义与功能存在以下关键区别: 1. 作用与本质差异 顶点(Vertex) 代表待优化的变量,例如: 位姿(如VertexSE3Expmap表示3D位姿,包含平移和旋转)空间点坐标(如VertexPointXYZ表示3D点)参数…...

stm32wb55rg (2) 阅读资料手册

阅读资料是嵌入式开发的必备技能&#xff0c;能够从资料中找到自己想要的技术信息&#xff0c;才是最为核心的技术能力。 nucleowb55rg板子的MCU为stm32wb55rg&#xff0c;这块板子的资料有很多&#xff0c;但有些内容可以边用边读&#xff0c;有些内容有必要预先掌握下。 下面…...

基于Python的携程国际机票价格抓取与分析

一、项目背景与目标 携程作为中国领先的在线旅行服务平台&#xff0c;提供了丰富的机票预订服务。其国际机票价格受多种因素影响&#xff0c;包括季节、节假日、航班时刻等。通过抓取携程国际机票价格数据&#xff0c;我们可以进行价格趋势分析、性价比评估以及旅行规划建议等…...

【LLM开发】Unigram算法

Unigram算法 参考&#xff1a;Unigram算法解释 参考书籍&#xff1a;How Can We Make Language Models Better at Handling the Diversity and Variability of Natural Languages Unigram 算法是一种基于概率的子词分词方法&#xff0c;与BPE算法、WordPiece算法不同&#xff…...

GD32F407单片机开发入门(十六)单片机IAP(在应用编程)详解及实战源码

文章目录 一.概要二.GD32F407VET6单片机IAP介绍1.GD32F407VET6单片机IAP基本原理2.GD32F407VET6单片机IAP基本流程3.Ymodem协议 三.配置一个BOOT工程四.配置一个APP工程五.工程源代码下载六.小结 一.概要 单片机上电或系统复位后&#xff0c;ARM Cortex-M4处理器先从0x0000 00…...

庙算兵棋推演AI开发初探(7-神经网络训练与评估概述)

前面我们提取了特征做了数据集、设计并实现了处理数据集的神经网络&#xff0c;接下来我们需要训练神经网络了&#xff0c;就是把数据对接好灌进去&#xff0c;训练后查看预测的和实际的结果是否一致——也就是训练与评估。 数据解析提取 数据编码为数据集 设计神经网络 -->…...

[实战] IRIG-B协议详解及Verilog实现(完整代码)

目录 IRIG-B(B码)协议详解及Verilog实现一、IRIG-B协议概述二、帧格式详细解析1. 码元类型与索引计数2. 时间编码字段3. 控制功能码元&#xff08;CF&#xff09;4. 纯二进制秒码&#xff08;SBS&#xff09; 三、编码与信号特性四、时间编码实现1. 时间参数转换2. 帧数据填充规…...

从传统制造到智能工厂:MES如何重塑电子制造业?

在“中国制造2025”战略的引领下&#xff0c;电子制造业正经历深刻变革。多品种小批量、工艺复杂度高、质量追溯严苛等需求日益凸显。由此&#xff0c;如何通过数字化手段实现生产透明化、质量可追溯和资源高效协同&#xff0c;成为行业转型的关键命题。 一、电子制造业转型痛…...

使用Curl进行本地MinIO的操作

前言 最近在做相关的项目中关于本地服务搭建和访问的技术验证&#xff0c;打进来最基本的数据访问&#xff0c;使用了C。可以进行&#xff1a;服务器的可用性检查、Bucket的创建、文件夹的创建、文件的上传、文件的下载、文件夹和Bucket的存在性检查等基本接口&#xff0c;对自…...