C++面向对象:继承!

前言

继承是面向对象三大特性之一,所有的面向对象的语言都具备这三个性质,我们之前已经介绍过了封装的相关概念,今天我们来学习一下第二大特性:继承。

一.继承的概念

什么是继承?
定义:继承(inheritance)机制是面向对象程序设计使代码可以复用的手段,它允许程序猿在保持原有基类(父类)特性的基础进行拓展,添加功能,这样产生新的类被称为派生类(子类)。
因此,我们可以发现,在继承中是有两个对象的,一个是被继承的对象,另一个是继承方。
那么,这两个对象分别是什么呢?

  • 被继承对象:父类/基类 (base)
  • 继承方:子类/派生类 (Derived)

1.1本质

继承的本质就是复用代码
现在,我们给出这么一个样例:
某个学校要设计一个学校人员管理系统。
那么,从人员上来说,我们就可以划分出如下两大类:教师和学生
而教师,又可以继续划分出如下:校长、年级主任、普通老师、后勤老师
而学生,也可以这么划分出如下:班长、各种委员、各种课代表、普通学生。

如果我们为每种人员都设计一个类,那么光设计类就要设计几十个了,因此我们要为每种人员设计一个class。而我们在设计的过程中可以发现一点:这些都TM是人阿!,都TM有名字年龄性别手机号!
也就是说,我们会写出如下的代码:


class Faculty//教职工
{
private:string name;int age;int sex;string PhoneNumber;
};
class student//教职工
{
private:string name;int age;int sex;string PhoneNumber;
};
//每个类都这么设计,只是类名不同而已
//......(此处省略一万个类)

这么写,实在是过于冗余了,让人很烦,每个教职工和学生都要把名字、年龄、性别、电话号定义一下。
那么,有没有一种方法,可以做到代码复用呢?
没错,这就是多态的作用!

1.2作用

多态的作用就是代码复用,让程序猿可以偷偷懒,少写两行代码。
在子类继承父类后,就可以继承到父类全部的公开/保护/私有属性,但是,除了私有内容的使用权限子类永远无法使用外,父类的公开/保护属性在子类中随着继承方式的不同会发生不同的变化,这点我们等等再谈。
实例:

class Faculty//教职工
{
public:Faculty():_name("kuzi"), _age(18), _sex("male"), _PhoneNumber("woshiyitiaokuzi"){cout << "我是一名教职工" << endl;};
protected:string _name;int _age;string _sex;string _PhoneNumber;
};
class Teacher : public Faculty
{Teacher(){Faculty();cout << "我是一名教师" << endl;};
};
int main()
{Faculty* a = new Faculty();Teacher* b = new Teacher();
}

下面我们可以通过调试窗口来观察一下子类Teacher的成员在这里插入图片描述

在这里插入图片描述
我们发现,子类中继承了父类的全部成员。

1.3实际样例

在我们的实际开发中,经常会用到继承
很经典的一个例子是,CPP中的IO流就是使用了继承
在这里插入图片描述

二.继承的定义

2.1格式

继承的格式是比较简单的,如下:

//子类:继承方式 父类
//eg:class a :public b 代表了a以public的方式继承了b。

在我们上述的例子中
在这里插入图片描述
ps:继承符是可以省略的,省略继承符时
使用class时,默认继承方式为public继承。

使用struct时,默认继承方式为private继承。

2.2权限

下面我们来介绍一下继承的方式:
在CPP中,继承有三种方式:

公有继承(public)、保护继承(protected)、私有继承(private)。

看到这里,你似乎觉得似曾相识,没错,这三种继承方式和类中的访问限定修饰符是一样的,不过这些符号在这里表示的是继承权限
那么,到了这里,我们就有必要去回忆一下这三个东西有什么用处了。

public:公开的,任何人都能访问
protected:保护的,只有当前类和子类可以访问
private:私有的,只有当前类可以访问
我们现在比较一下权限,显而易见的是:public>protected>private
之前我们说过一句话:保护protected只有在继承中才能体现出价值来,在别的场景下和private的作用是一样的。

也就是说,我们有三种访问权限和三种继承权限。
根据排列组合,我们可以列出如下的多种搭配方案:

父类成员/继承权限publicprotectedprivate
父类的public成员子类对象可见,子类也可见子类对象不可见,子类中可见子类对象不可见,子类中可见。
父类的protected成员子类对象不可见,子类中可见子类对象不可见,子类中可见子类对象不可见,子类中可见
父类的private成员都不可见都不可见都不可见

总结一下:

  • 无论是哪种继承方式,父类中的private成员始终不可被[子类/外部]访问。
  • 当子类对象试图访问父类成员时,依据min(父类成员权限,子类继承权限)原则,只有最终权限为public时,子类对象才能访问。

下面我们通过代码进行实践:

class A
{
public:int _a;
protected:int _b;
private:int _c;
};
class B :public A
{B(){cout << _a << endl;cout << _b << endl;cout << _c << endl;}
};
int main()
{B b;b._b;
}

我们可以将这段代码复制到你的编辑器中,不出意料的话,会报出如下错误:
在这里插入图片描述
因此,我们可以验证:

  • public继承中,我们可以访问到protect成员,但是无法在外部使用。

下面我们再来验证protect继承和private继承:
protect继承:
在这里插入图片描述
private继承:
在这里插入图片描述
我们发现,protect和private的效果是一样的,因此我们认为C++的继承设计是复杂了。
那么,我们应该如何访问到父类的私有成员呢?
答案:我们在父类中设计相应的函数,我们间接调用即可完成任务。
如下:

class A
{
public:int _a;int get_c(){return _c;}
protected:int _b;
private:int _c;
};
class B :public A
{
public:B(){cout << _a << endl;cout << _b << endl;//cout << _c << endl;}
};
int main()
{B b;b.get_c();//b._a;
}

这里,我们通过设计了get_c()函数,即可得到私有成员。

2.3使用

在经过刚刚的学习之后,我们对继承的了解应该深入了很多,那么,我们在设计的时候,如何才能将权限使用的很优雅呢?

对于只想自己查看的成员,设计为private,对于想共享给子类使用的成员,设为protected,其他成员都设为public。
下面给大家举个例子:

有个人叫裤子,他是一名CSDN博主,他写的文章是公开全网的,他的文章收益是只限于家庭成员使用的,他的隐私小网站是只允许他自己知道的。
那么,我们在设计时,即可将文章信息设计为public、收益设计为protected、小网站设计为private
如下:

class Kuzi
{
public:string article;//文章是写给大家看的
protected:string money;//钱是家里人都可以用的
private:string website;//byd我的小网站你还想看?
};
class KuziSon :public Kuzi
{KuziSon(){cout << "我是裤子的儿子" << endl;cout << "爸爸的文章是:" << article << endl;cout << "爸爸的钱是:" << money << endl;cout << "我看不到爸爸的小网站" << endl;}
};
class ZhangSan
{ZhangSan(){                                          //匿名对象调用cout<<"我是小明,我只能看到裤子的文章:"<<Kuzi().article<< endl;}
};

三.继承的作用域

子类和父类的作用域是不同的,下面我们将通过”隐藏“这个概念详谈

3.1隐藏

假如,在子类中出现和父类相同的变量/函数名时,会怎么样呢?

会发生”隐藏“,默认调用子类的变量/函数名。
这个概念还被称为”重定义

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
    也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。

下面我们通过代码来体验一下以上规则:

class Base
{
public:void func(){cout << "Base::val==" << val << endl;}
private:int val = 3;
};
class Derived :public Base
{
public:void func(){cout << "Derive::val==" << val << endl;}
private:int val = 4;
};
int main()
{Derived a;a.func();return 0;
}

输出结果如下:

Derive::val==4

我们发现,父类中的方法和成员都被隐藏了,执行的是子类方法,输出的是子类成员。

总结:
当子类当中的方法出现隐藏行为时,会优先执行子类当中的方法;
当子类当中的成员出现隐藏行为时,会优先选择当前作用域的成员(局部优先原则)
因此,编译器的搜索逻辑是:先搜索当前作用域,再搜索父类作用域。
因此,子类的作用域和父类的作用域是两个不同的作用域。

那么,我们应该如何显式的使用父类的方法或成员呢?

  • 使用域作用限定符限定搜索的作用域。

如下:

class Base
{
public:void func(){cout << "Base::val==" << val << endl;}
protected:int val = 3;
};
class Derived :public Base
{
public:void func(){cout << "Derive::val==" << Base::val << endl;}
private:int val = 4;
};
int main()
{Derived a;a.func();return 0;
}

结果:

Derive::val==3

这样,我们便打印出了父类的val值。
这里,我们要给大家提出几点使用继承中函数的建议:

  1. 只要函数名相同就会构成隐藏,和返回值/参数无关。
  2. 隐藏可能会干扰到调用者的真实意图
  3. 因此我们要尽可能少的设计隐藏

四.基类与派生类对象的赋值转换

在继承中,我们允许将子类对象直接赋值给父类对象,但是不允许和父类对象直接赋值给子类。
这个赋值是直接赋值,是编译器允许的,是不需要重载赋值运算符的。

int main()
{Base a;Derived b;a = b;//合法b = a;//不合法
}

其实这种行为是切片,子类对象在赋值给父类对象时,可以触发切片机制,从而完成赋值

4.1切片

切片操作其实也很简单,就相当于是现在有一个坏了的苹果,现在我们要吃掉这个苹果,就需要切掉坏的部分,只吃好的部分。
因此,最终的结果就是我们拿了把菜刀,把坏的切了,把好的吃了。
对于我们而言:好的部分是原本属于它的,坏的部分是后来才有的。
对于对象而言也是一样的,如下:
在这里插入图片描述

这个行为其实非常容易理解,父类有的子类全部都有,但是子类有的父类并不一定全有,因此我们可以用子类赋值给父类,只需要切掉不用的部分即可。
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片,或者切割。寓意把派生类中父类那部分切来赋值过去。
具体原因:

子类中+一个父类指针,刚好跳过了一整个父类,我们把这一整个父类拿出来即可赋值给父类成员,完成切片操作。

切片的效率是非常高的,这是因为切片并不需要产生临时对象,而且只需要进行一步指针操作!

切片的具体实现,后续再专门写一篇文章详谈。

五.派生类的默认成员函数

派生类也是类,是类就会有类的默认成员函数。
但,子类是在父类的基础上构建的类,那么,我们在实际操作的时候,是否会出现一些奇奇怪怪的现象呢?
在这里插入图片描述
这里我们介绍如下现象:

  1. 派生类的构造函数调用之前必须要调用父类的构造函数构造出父类来。如果父类没有默认构造,则必须在子类的构造函数的初始化列表阶段显示调用。
  2. 派生类的构造拷贝函数必须要调用基类的构造拷贝函数才能够完成基类的拷贝初始化。(浅拷贝问题)
  3. 派生类的赋值重载必须要调用基类的赋值重载才能完成基类的赋值。(浅拷贝问题)
  4. 派生类的析构函数会在调用后自动调用父类的析构函数清理基类成员。(栈的先进后出)
  5. 派生类先初始化调用基类构造再调用派生类构造(栈的先进后出)
  6. 再一些场景下析构函数需要构成重写,重写需要三同(再多态中会了解)。编译器会对析构函数名做出特殊处理,处理为:destrutor()。这时父类析构不加virtual的情况下会和子类的析构函数构成隐藏关系

5.1隐式调用

子类在继承父类后,构建子类对象时会先调用父类的默认构造函数,子类对象销毁前,还会自动调用父类的析构函数。
我们可以写出如下代码:

class Person
{
public:Person() { cout << "Person()" << endl; }~Person() { cout << "~Person()" << endl; }
};
class Student :public Person
{
public:Student() { cout << "Student()" << endl; }~Student() { cout << "~Student()" << endl; }
};
int main()
{Student a;return 0;
}

打印结果如下:

Person()
Student()
~Student()
~Person()

我们可以看到:
我们的子类构造之前先调用了父类的构造函数。
父类的析构之前,默认调用了子类的析构函数。
这里我们需要注意的是:调用是编译器帮我们完成的,需要父类含有默认的构造函数,如果不含有,则寄!
这里补充一个知识点:
如果我们显示的写了构造函数,但是并没有默认的构造函数,这是我们可以用这串代码来帮助我们生成默认的构造函数。

class MyClass {
public:MyClass() = default; // 显式要求编译器生成默认构造函数MyClass() = delete;//显示要求编译器不生成默认构造函数
};

5.2显示调用

我们在刚刚已经提过了,我们的拷贝构造要先调用父类的,赋值也是。
这是为什么呢?这是因为我们如果不调用一下父类的,则会出现浅拷贝问题等问题。

这是因为,我们子类的赋值重载运算符只会将子类的属性赋值过去,但是父类的属性并不会操作。因此我们需要调用父类的赋值重载运算符来避免这个问题。
如下例,将会出现浅拷贝问题:

class Base {
public:int* data;Base() { data = new int(5); }~Base() { delete data; }Base& operator=(const Base& other) {if (*this != other) {*data = *(other.data);}return *this;}
};class Derived : public Base {
public:// 未调用 Base::operator=,这会导致基类部分浅拷贝Derived& operator=(const Derived& other) {if (this != &other) {// 假设 Derived 有自己的成员赋值逻辑}return *this;}
};

5.3为什么派生类调用赋值重载运算符时要先调用父类的赋值重载?

在 C++ 中,派生类调用赋值重载运算符时,先调用父类的赋值重载运算符,主要是为了保证基类部分的成员变量能够正确赋值。因为派生类不仅包含自己的成员变量,还继承了基类的成员,因此必须正确处理基类的赋值操作,保证对象状态的一致性。


1. 继承关系中的赋值操作

派生类继承了基类的成员变量和函数。如果派生类没有显式定义赋值重载运算符,编译器会自动生成一个赋值运算符,包括基类部分的赋值。如果派生类显式定义了赋值重载运算符,则通常需要手动调用基类的赋值运算符来确保基类成员被正确赋值。我们可以通过域作用限定符显示调用。

示例代码
class Base {
protected:int baseValue;
public:Base(int val = 0) : baseValue(val) {}Base& operator=(const Base& other) {std::cout << "Base assignment operator called" << std::endl;if (this != &other) {baseValue = other.baseValue;  // 基类的赋值逻辑}return *this;}
};class Derived : public Base {
private:int derivedValue;
public:Derived(int bVal = 0, int dVal = 0) : Base(bVal), derivedValue(dVal) {}Derived& operator=(const Derived& other) {std::cout << "Derived assignment operator called" << std::endl;if (this != &other) {Base::operator=(other);  // 调用基类的赋值运算符derivedValue = other.derivedValue;  // 派生类的赋值逻辑}return *this;}
};

最后一点,析构函数必须写成虚函数,这一点会在讲解多态时谈到,并且非常重要。

六.继承与友元函数

直接上结论:友元关系不能被继承
这一点也非常容易理解。假如你爸有一个朋友,这个朋友很喜欢逗小孩,有一天你爸爸让他带你玩,给你带来了非常严重的心理阴影,因此你不会认为他是你的朋友。
我们直接用代码来体验一下这一点即可:

class Base
{friend void MyPrint();
private:static const int a = 10;
};
class Derived :public Base
{
private:static const int b = 100;
};
void MyPrint()
{cout << Base::a << endl;cout << Derived::b << endl;
}
int main()
{MyPrint();return 0;
}

我们会发现出现了如下的报错信息:

成员 “Derived::b” (已声明 所在行数:27) 不可访问

如果我们想让Print函数也可以访问子类的私有成员,那么我们需要将其也声明为子类的友元函数。
代码如下:

class Base
{friend void MyPrint();
private:static const int a = 10;
};
class Derived :public Base
{friend void MyPrint();
private:static const int b = 100;
};
void MyPrint()
{cout << Base::a << endl;cout << Derived::b << endl;
}

七.继承与静态成员

对于这点,我们需要记住的仅仅只是:静态成员是唯一存在的。
在我们的上一个例子中,虽然我们并没有定义成员,但是我们依旧能够打印出静态变量的值,这正是因为它是独立存在于静态区的,而静态区的生命周期很长,一般在程序开始时被创建,在结束时被销毁。
也就是说,如果父类中有一个静态变量,那么子类在继承后,也是可以共享这个变量的。
我们可以利用这个特性完成下题:
问题:请统计创建了多少个父类子类对象?

class Base
{
public:Base(){num++;}static int num;
};
int Base::num = 0;
class Derived :public Base
{
public:Derived(){num++;}
};
int main()
{Base a;Base b;Derived c;//父类1+子类1cout << Base::num << endl;return 0;
}

打印结果:

4

八.菱形继承

这里我们先介绍一下单继承和多继承
单继承:一个子类只能继承一个父类
多继承:一个子类可以继承多个父类(JAVA中不支持此特性)
C++支持多继承,即一个子类可以继承多个父类。多继承在带来了巨大的便捷性的同时也带来了一个巨大的坑:菱形继承问题

8.1概念

C++是允许出现多继承的情况的,给大家打个比方:
神是不用吃饭的。
那么,如果出现以下的继承情况:
在这里插入图片描述
这里,普通人1和2通过奇遇相识并学习了神的道法,修得了不用吃饭之道。
他们恋爱了,生下了普通人3。这时普通人3就继承了普通人1和2的属性,但是出现了一个问题:
BYD我爸我妈都长生,我不用吃饭是继承的谁。

8.2现象

关于多继承的父类初始化问题:谁先被声明,谁就会先被初始化,这个与继承顺序是无关的。
现在我们将刚刚说的转换为代码,如下:

class God
{
public:void eat(){cout << "老子是神,老子还用吃饭?" << endl;}
};
class Person1 : public God
{//......
}; 
class Person2 : public God
{//......
};
class Person3 : public Person1,public Person2
{//......
};
int main()
{Person3 a;a.eat();
}

这时,我们会有如下的报错信息:
在这里插入图片描述

8.3原因

在cpp中,我们将这个问题称为数据冗余和二义性问题。这是因为继承时出现了多继承,父亲和母亲都有不用吃饭的属性,这时孩子就会有两个不用吃饭的属性,编译器就会不知所措,不知道孩子的不用吃饭的属性是继承的谁。
在这里插入图片描述

8.4解决方法

想要解决二义性的问题是比较简单的,我们直接通过**::**限定访问域即可。

int main()
{Person3 a;a.Person1::eat();
}

这样,我们就可以解决二义性的问题。下面我们就要着手于解决数据冗余的问题。
其实,这里我们真正的解决方法是:虚继承
PS:虚继承上的专门用来解决菱形继承问题的,与多态中的虚函数没有关系。
虚继承:在菱形继承的腰部继承处加上virtual修饰父类。

class God
{
public:void eat(){cout << "老子是神,老子还用吃饭?" << endl;}
};
class Person1 :virtual public God
{//......
}; 
class Person2 :virtual public God
{//......
};
class Person3 : public Person1,public Person2
{//......
};
int main()
{Person3 a;return 0;
}

这时,我们就可以解决菱形继承的数据冗余和二义性问题。
问题:虚继承是如何解决菱形继承问题的?

  • 利用虚基表将冗余的数据存储起来,并将冗余数据合并为一份。
  • 原来存储冗余数据的位置,现在用来存储虚基表指针。
    关于虚基表,我们后续再专门详谈。

九.补充

虽然我们学习了继承,但是并不是说我们就一定要使用继承。
我们还可以使用组合的方法来完成类似的效果。
继承,其实只是为了使用父类的成员。
同样的, 我们使用组合也可以达到一样的效果。
下面简单介绍下两类的区别

  • 公有继承:is-a 高耦合,可以直接使用父类成员
  • 组合:has-a 低耦合,可以间接使用父类成员。

在实际的项目中,我们更推荐使用组合的方式,因为这样可以做到解耦,避免因为父类的改动而直接影响到子类。
如下:

class A {};
class B :public A{};
class C
{
private:A _a;//创建出A类对象,之后可以间接使用。
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/439689.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第一套)

一.题目分析 &#xff08;1&#xff09;.题目 &#xff08;2&#xff09;.题目分析 1.串口功能分析 a.串口接收车辆出入信息&#xff1a;通过查询车库的车判断车辆是进入/出去 b.串口输出计费信息&#xff1a;输出编号&#xff0c;时长和费用 c.计算停车时长是难点&#x…

【IO】多路转接Select

一、初识 select 系统提供 select 函数来实现多路复用输入/输出模型. select 系统调用是用来让我们的程序监视多个文件描述符的状态变化的;程序会停在 select 这里等待&#xff0c;直到被监视的文件描述符有一个或多个发生了状态改变; select 函数原型 C #include <sys/…

Python+Django微信小程序前后端人脸识别登录注册

程序示例精选 PythonDjango微信小程序前后端人脸识别登录注册 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjango微信小程序前后端人脸识别登录注册》编写代码&#xff0c;代码整…

基于SpringBoot+Vue+MySQL的在线学习交流平台

系统展示 用户前台界面 管理员后台界面 系统背景 随着互联网技术的飞速发展&#xff0c;在线学习已成为现代教育的重要组成部分。传统的面对面教学方式已无法满足广大学习者的需求&#xff0c;特别是在时间、地点上受限的学习者。因此&#xff0c;构建一个基于SpringBoot、Vue.…

2024年最新大模型LLM学习路径全解析!看完你就是LLM大师

ChatGPT的出现在全球掀起了AI大模型的浪潮&#xff0c;2023年可以被称为AI元年&#xff0c;AI大模型以一种野蛮的方式&#xff0c;闯入你我的生活之中。 从问答对话到辅助编程&#xff0c;从图画解析到自主创作&#xff0c;AI所展现出来的能力&#xff0c;超出了多数人的预料&…

华为eNSP:端口隔离

一&#xff0c;什么是端口隔离 端口隔离是一种网络配置技术&#xff0c;用于将不同的网络设备或用户隔离在不同的虚拟局域网&#xff08;VLAN&#xff09;中&#xff0c;以实现网络流量的隔离和安全性提升。通过在交换机或路由器上配置端口隔离&#xff0c;可以将连接到同一设…

Java多线程(2)—线程创建

Java多线程(2)—线程创建 一、线程创建简介 在Java中&#xff0c;创建线程可以通过两种主要方式&#xff1a;继承 Thread​ 类、实现 Runnable​ 、实现Callable ​接口和线程池。 ​ ‍ 二、创建方式 2.1 继承 Thread 类 示例1 ♠①&#xff1a;创建一个类继承 Thread…

【工程测试技术】第6章 信号处理初步,频谱分析,相关系数

目录 6.1 数字信号处理的基本步骤 6.2 离散信号及其频谱分析 6.2.1 概述 6.2.2 时域采样、混叠和采样定理 6.2.3 量化和量化误差 6.2.4 截断、泄漏和窗函数 6.2.5 频域采样、时域周期延拓和栅栏效应 6.2.6 频率分辨率、整周期截断 6.3 相关分析及其应用 6.3.1 两…

前端学习第二天笔记 CSS选择 盒子模型 浮动 定位 CSS3新特性 动画 媒体查询 精灵图雪碧图 字体图标

CSS学习 CSS选择器全局选择器元素选择器类选择器ID选择器合并选择器 选择器的优先级字体属性背景属性文本属性表格属性表格边框折叠边框表格文字对齐表格填充表格颜色 关系选择器后代选择器子代选择器相邻兄弟选择器通用兄弟选择器 CSS盒子模型弹性盒子模型父元素上的属性flex-…

STM32三种启动模式:【详细讲解】

STM32在上电后&#xff0c;从那里启动是由BOOT0和BOOT1引脚的电平决定的&#xff0c;如下表&#xff1a; BOOT模式选引脚启动模式BOOT0BOOT1X0主Flash启动01系统存储器启动11内置SRAM启动 BOOT 引脚的值在重置后 SYSCLK 的第四个上升沿时被锁定。在重置后,由用户决定是如何设…

基于springboot vue3 在线考试系统设计与实现 源码数据库 文档

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm springcloud等开发框架&#xff09; vue .net php phython node.js uniapp小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆…

基于元神操作系统实现NTFS文件操作(六)

1. 背景 本文主要介绍$Root元文件属性的解析。先介绍元文件各属性的属性体构成&#xff0c;然后结合读取到的元文件内容&#xff0c;对测试磁盘中目标分区的根目录进行展示。 2. $Root元文件属性的解析 使用每个属性头偏移0x04-0x07处的值可以从第一个属性开始依次定位下一个…

一款基于 Java 的可视化 HTTP API 接口快速开发框架,干掉 CRUD,效率爆炸(带私活源码)

平常我们经常需要编写 API&#xff0c;但其实常常只是一些简单的增删改查&#xff0c;写这些代码非常枯燥无趣。 今天给大家带来的是一款基于 Java 的可视化 HTTP API 接口快速开发框架&#xff0c;通过 UI 界面编写接口&#xff0c;无需定义 Controller、Service、Dao 等 Jav…

在线教育的未来:SpringBoot技术实现

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理微服务在线教育系统的相关信息成为必然。开…

【可答疑】基于51单片机的PWM控制智能台灯设计(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

什么软件能指定usb端口禁用?五款电脑USB端口禁用软件!(热门分享)

什么软件能指定usb端口禁用&#xff1f; USB端口&#xff0c;作为电脑与外部设备连接的重要接口&#xff0c;其安全性日益受到企业的重视。 为了有效防止数据泄露和未经授权的设备接入&#xff0c;指定USB端口禁用成为了许多企业的迫切需求。 本文&#xff0c;将介绍五款热门…

MySQL 用户管理

一.用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中。 host&#xff1a; 表示这个用户可以从哪个主机登陆&#xff0c;如果是localhost&#xff0c;表示只能从本机登陆user&#xff1a; 用户名authentication_string&#xff1a; 用户密码通过password函…

Python | Leetcode Python题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; class Solution:def circularArrayLoop(self, nums: List[int]) -> bool:n len(nums)def next(cur: int) -> int:return (cur nums[cur]) % n # 保证返回值在 [0,n) 中for i, num in enumerate(nums):if num 0:continueslow, fas…

中小企业做网站需要考虑哪些因素?

中小企业在建设网站时&#xff0c;需要考虑的因素有很多。以下是一些主要考虑因素的介绍&#xff1a; 明确建站目的&#xff1a;中小企业需要明确自己建立网站的目的。是为了展示企业形象、推广产品&#xff0c;还是提供客户服务&#xff1f;不同的目的将决定网站的设计和功能…

MOELoRA —— 多任务医学应用中的参数高效微调方法

人工智能咨询培训老师叶梓 转载标明出处 在医疗场景中&#xff0c;LLMs可以应用于多种不同的任务&#xff0c;如医生推荐、诊断预测、药物推荐、医学实体识别、临床报告生成等。这些任务的输入和输出差异很大&#xff0c;给统一模型的微调带来了挑战。而且LLMs的参数众多&…