0基础入门C++之类和对象中篇

目录

      • 1.类的6个默认成员函数
      • 2.构造函数
        • 2.1概念
        • 2.2特性
      • 3.析构函数
        • 3.1概念
        • 3.2特性
      • 4.拷贝构造函数
        • 4.1概念
        • 4.2 特性
      • 5.赋值运算符重载
        • 5.1运算符重载
        • 5.2赋值重载
        • 5.3赋值运算符重载特性
      • 6.const成员
      • 7.取地址及const取地址操作符重载

1.类的6个默认成员函数

通过类和对象上篇的学习,我们知道知道如果一个类中没有成员变量,也没有成员函数,啥也没有,那我们把它叫做空类。
比如 :class Date {};

那么空类中真的什么都没有吗?

并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

默认成员函数:即用户没有显式实现,编译器自动生成的成员函数称。

在这里插入图片描述

2.构造函数

2.1概念

现在有这样一个类:

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/"  << _month  << "/"  <<  _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2;d1.Init(2023,9,5);d1.Print();d2.Init(2023,8,18);d2.Print();return 0;
}

那对于一个类来说,我们实例化出来对象之后一般会对其进行一个初始化,但是有时候我们可能会忘记初始化,直接就对对象进行一些操作,不初始化直接用可能就会出现问题。

在这里插入图片描述
那么针对以上情况,C++给我们提供一种方法解决这个问题。

这个方法就是构造函数。构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

2.2特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

其特征如下:

  1. 函数名与类名相同。

也就是说定义好一个类,它的构造函数的函数名就已经确定,跟当前类的类名是相同的。

  1. 无返回值。

要注意这里说的无返回值不是说返回类型是void,而是根本就不写返回类型。

  1. 对象实例化时编译器自动调用对应的构造函数。

通过构造函数我们初始化对象就不用再手动初始化了,实例化对象时编译器会自动调用其对应的构造函数。

  1. 构造函数可以重载。

那么接下来我们就给上面的Date类写一个构造函数。

  Date(){_year = 1;_month = 1;_day = 1;}
  1. 无参构造函数

在这里插入图片描述
通过上图运行结果我们看出:这次我们并没有调用初始化函数,但是打印出来不是随机值,而是我们在构造函数中给定的值,说明我们实例化对象的时候确实自动调用构造函数进行初始化了。

  1. 带参构造函数
Date(int year, int month, int day){_year = year;_month = month;_day = day;}

在这里插入图片描述

注意: 如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明.

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成

也就是说,构造函数不一定非要自己写,如果我们自己没有定义构造函数,编译器会自动生成一个。只不过是无参的。

那编译器会自动生成的话,我们以后是不是就不用自己写构造函数了?

答案是不可以

将上文中自己写的构造函数注释掉,直接运行程序:
在这里插入图片描述
我们可以看到调用编译器自动生成的构造函数是随机值。

实际上这个地方大家可以认为是C++在设计时出现了问题。

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...(包括各种指针类型),自定义类型就是我们使用class/struct/union等自己定义的类型
而编译器自动生成的构造函数不会对内置类型进行处理,对于自定义类型会处理,怎么处理?会去调用该自定义类型对应的默认构造函数

我们再看一个例子:

class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

这里的Date类它的成员变量里既有内置类型又有自定义类型。
但是我们现在并没有给Date类写构造函数,那我们在main函数里直接拿Date去创建一个对象,它自然就会去调用编译器自动生成的构造函数,那内置类型不做处理,这里还有一个自定义类型Time _t;,对于自定义类型,编译器会自动去调用它对应的默认构造函数。
我们运行一下验证一下结果:
在这里插入图片描述

那难道说内置类型不写构造函数就没法初始化了吗?

注意: C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
在这里插入图片描述
在这里插入图片描述

  1. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意: 无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

3.析构函数

3.1概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?

析构函数: 与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

下面我们举一个例子来帮助大家理解:

typedef int DataType;
class Stack
{
public://构造函数Stack(size_t capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc fail");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}private:DataType* _array;int _capacity;int _size;
};int main()
{Stack s;s.Push(1);s.Push(2);return 0;
}

这里的对象s需要我们自己去销毁吗?
答案是不需要,因为s是定义在栈区上的局部变量,程序结束,它就随着main函数的栈帧自动销毁。
那析构函数的作用是啥呢?完成对象中资源的清理工作,什么意思?
像栈这样的对象,它里面是有在堆上动态开辟的空间,那经过C语言的学习我们都知道,这些空间是需要我们手动去释放的,否则可能会导致内存泄漏。
所以说,析构函数就是来帮我们干这件事情的。

3.2特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~。

一个类定义好之后,它的析构函数的函数名也是确定的,即在类名前面加上“~”。
“~”在C语言中是按位取反,表示它的功能和构造函数是相反的。

  1. 无参数无返回值类型
  2. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  3. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

下面我们就给刚才的栈写一个析构函数

~Stack(){free(_array);_array = NULL;_capacity = 0;_size = 0;}

为了方便观察是否自动调用了析构函数我们可以在代码中加入一行打印:
在这里插入图片描述

此时我们main函数里并没有显示调用~Stack函数
在这里插入图片描述
运行结果如下:
在这里插入图片描述

  1. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
typedef int DataType;
class Stack
{
public://构造函数Stack(size_t capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc fail");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << "~Stack" << endl;free(_array);_array = NULL;_capacity = 0;_size = 0;}private:DataType* _array;int _capacity;int _size;
};class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Stack _s;
};int main()
{// Stack s;// s.Push(1);// s.Push(2);Date d1;return 0;
}

这里我们没有给Date显式定义析构函数,d1声明周期结束时,就会调用编译器自己生成的默认析构函数,那里面的内置类型不做处理,而自定义类型Stack _s;申请的资源需要清理,编译器自己生成的默认析构函数会调用Stack 类的析构函数:
在这里插入图片描述

  1. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

4.拷贝构造函数

4.1概念

我们再来回顾一下之前创建的日期类:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/"  << _month  << "/"  <<  _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{Date d1;return 0;
}

现在有这样一个问题:如果我们现在想再创建一个对象,使这个对象和d1一样,或者说是d1的一份拷贝,应该怎么实现呢?

经过上面的学习,相信老铁们很容易想到,我们想创建一个和d1一样的新对象,可以用d1去初始化创建出来的新对象啊。是不是把构造函数的参数类型设置成类对象的类型就行了。
这就是拷贝构造。

拷贝构造函数: 只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

4.2 特性

拷贝构造函数也是特殊的成员函数,其特性如下:

  1. 拷贝构造函数是构造函数的一个重载形式。

那么我们现在先来写一个拷贝构造函数:

   Date(Date d){_year = d._year;_month = d._month;_day = d._day;}

那这样就可以了吗?
在这里插入图片描述

这里不行的原因是:在之前拷贝构造函数概念中我们提到参数类型必须是类类型对象的引用。
在这里插入图片描述

那为什么必须是类类型对象的引用呢?

  1. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

在这里为什么会引发无穷递归呢?
在这里插入图片描述

此外对一个对象拷贝构造也可以这样写:

这里是引用

除此之外,我们还需要注意:

拷贝构造函数形参一般用const修饰:
在这里插入图片描述
相信这一点大家不难理解,形参是用来初始化我们新创建的对象的,加个const修饰形参d不会被修改。此外加上const 若传来的参数是const修饰的,我们依然可以接收。

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

那么默认生成的拷贝构造函数是否可靠呢?

这里我们再来看一下日期类:

首先我们将刚才实现的拷贝构造函数注释掉:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//    Date(const Date& d)//   {//       _year = d._year;//       _month = d._month;//       _day = d._day;//   }void Print(){cout << _year << "/"  << _month  << "/"  <<  _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{// Date d1(2023,9,5);// Date d2(2023,8,18);//d1.Init(2023,9,5);Date d1;Date d2(d1);Date d3 = d1;d1.Print();//d2.Init(2023,8,18);d2.Print();d3.Print();return 0;
}

我们看一下结果
在这里插入图片描述

看到这里拷贝构造函数能一直可靠吗?

下面我们再来看一下Stack类:

typedef int DataType;
class Stack
{
public://构造函数Stack(size_t capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc fail");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << "~Stack" << endl;free(_array);_array = NULL;_capacity = 0;_size = 0;}private:DataType* _array;int _capacity;int _size;
};int main()
{Stack s;s.Push(1);s.Push(2);Stack s2(s);return 0;
}

对于Stack类我们也没有写拷贝构造函数,我们运行一下看一下结果:
在这里插入图片描述
会发现这里程序挂了, 那这里挂了的原因是什么呢?

其实这里根本原因就是出现在特性3上。

在特性3中有这样一句话:
在这里插入图片描述
在这里其实就是对逐个成员变量依次进行拷贝,里面存的是啥就把啥拷过去。

我们来对比一下Date类和Stack这两个类的拷贝:

对于Date类,浅拷贝是没有问题的。
在这里插入图片描述
一共12字节的内容依次拷贝过去就行

对于Stack类浅拷贝是存在问题的:
在这里插入图片描述
在这里出了作用域就会调用析构函数,而两个s1和s2中指针指向的空间就会被free两次,同样的我们在s1中入栈数据,s2里面就也有数据了(因为它俩用的是同一块空间),然后如果我们再用st2去入栈数据,此时s1_size前面已经++过,但是s2_size前面还是0,这样s2入的数据就把之前s1入的数据给覆盖了。

注意:
这里是s2先析构,我们知道s1和s2都是在栈上的(栈区),那栈区之所以叫栈区就是因为它在这个地方栈帧的建立也是遵循先进后出的这个顺序的,即后定义的会先进行析构。s2先析构,那堆上的这块空间就被释放了,但是接下来st也会进行它的析构,而此时虽然s1还保留了这块空间的地址,但是这块空间已经被释放,所以s1就是个野指针了。
所以为什么程序崩溃了,就是我们这里对野指针进行free了
在这里插入图片描述

因此:

类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

  1. 拷贝构造函数典型调用场景:

使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象

5.赋值运算符重载

5.1运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为: 关键字operator后面接需要重载的运算符符号。
函数原型: 返回值类型 operator操作符(参数列表)

下面我们依旧以日期类为例:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/"  << _month  << "/"  <<  _day << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{Date d1(2023,9,5);Date d2(2023,8,18);d1.Print();d2.Print();return 0;
}

现在有两个对象d1,d2,大家思考一个问题,现在我们想比较这两个对象是否相等,要怎么实现呢?相信大家很容易想到用一个函数实现:

bool Equal(const Date& x1, const Date& x2)
{//...
}

但是在C++引入运算符重载之后呢,就使得我们还能够这样去实现:

bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1.day == d2.day;
}

这里会有一个小问题:
在这里插入图片描述
造成此问题的原因是:
Date类的这3个成员变量是私有的(private),所以在类外面是不能访问的。
那怎么解决?
我们可以在类里写一个Get方法(函数),通过Get方法来访问,或者直接把private访问限定符去掉。
我们这里先把private注释下:
在这里插入图片描述

现在我们来调用一下:

这里是引用
注意:因为这里<<的优先级比==高,所以要加括号。

但是这里直接重载到了全局,我们把成员变量全部公有了,封装性又如何体现呢?

所以这里比较好的一种方法是:我们直接重载到类里面,即重载成成员函数。

但是这里又出现了一个小问题如果直接把函数封装在类里:

这里是引用
这里我们重载的是==运算符,正常情况下只有两个操作数,所以只需要两个参数就够了。
那这里不就是两个参数嘛?
不要忘了,这里还有一个隐藏参数。什么隐藏参数,就是this指针。
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象)
所以我们这里只需给一个参数。

bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}

注意:

  1. 不能连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符至少有一个类类型的参数
  3. 用于内置类型的运算符,其含义不能重载改变,例如:内置的整型+,不能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1个,因为成员函数的第一个参数为隐藏的this
  5. .* :: sizeof ?: .注意这5个运算符不能重载,这个经常在笔试选择题中出现。

5.2赋值重载

参数类型:const 类对象的引用,传递引用可以提高传参效率
返回值类型:类类型&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
最好检测一下是否是自己给自己赋值,并进行一下处理
返回*this:返回的结果用于支持连续赋值

那么日期类的赋值重载就是:

	Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}

但是有时候呢不排除有人可能会把自己赋值给自己,于是它调用函数白白进行了一次拷贝,我们对此进行改进

	Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}

5.3赋值运算符重载特性

  1. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝(浅拷贝)。

注意:默认生成的赋值重载对于内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用其对应类的赋值运算符重载完成赋值。

那么下面这种情况会调用拷贝构造还是赋值重载?

这里是引用
这里用了赋值=,但是是拷贝构造。
什么时候是调赋值重载呢?
是我们用已经实例化出来的对象进行相互赋值的时候,调用赋值重载。而当我们用一个已经实例化出来的对象去初始化一个新对象的时候,调的是拷贝构造。

  1. 赋值运算符只能重载成类的成员函数不能重载成全局函数

原因:赋值重载如果在类里不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
在这里插入图片描述

6.const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数 隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

以刚才的赋值重载函数为例:

Date& operator=(const Date& d) const{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}

在这里插入图片描述

7.取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!

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

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

相关文章

docker安装redis7-哨兵模式

说明 系统&#xff1a;CentOS7.9 redis&#xff1a;7.0.5 由于资源问题本次的部署全部在一台宿主机上通过启动不同的docker容器来完成部署。 搭建哨兵模式之前&#xff0c;首先搭建好主从模式&#xff0c;1主2从&#xff0c;可以参考上一篇文章&#xff1a;docker安装redis…

Linux 进程间通信——消息队列

一、消息队列的原理 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型&#xff0c;接收进程可以独立接收含有不同类型值得数据库。 消息实际上是一个数据块&#xff0c;这个数据块是一个结构体&#xff0c;结构体由自己命名。消…

HTML笔记(3)

表单标签 用于登录、注册界面&#xff0c;以采集用户输入的信息&#xff0c;把信息采集到之后&#xff0c;用户一点按钮&#xff0c;就会把这些信息发送到服务端&#xff0c;服务端就可以把这些数据存储到数据库&#xff0c;所以表单是一个非常重要的html标签&#xff0c;它主要…

Appium 2安装与使用java对Android进行自动化测试

文章目录 1、Appium 2.1安装1.1、系统要求1.2、安装Appium2.1服务1.3、安装UiAutomator2驱动1.4、安装Android SDK platform tools1.5、下载OpenJDK 2、Android自动代码例子2.1、安装Android自动化测试元素定位工具Appium Inspector2.2、编写android app自动化测试代码和使用ex…

常见架构类型

目录 1.单机架构 2.应用数据分离架构 3.读写分离架构 4.冷热分离架构 5.垂直分库架构 6.微服务架构 7.容器编排架构 1.单机架构 单机架构是简单的将应用服务和数据库服务部署到同一台机器上。 缺点&#xff1a;存在很大的性能限制。 2.应用数据分离架构 引入负载均衡&a…

Git概述

目录 一、什么是Git 二、什么是版本控制系统 三、Git和SVN对比 SVN集中式 SVN优缺点 Git分布式 Git优缺点 四、Git工作流程 四个工作区域 工作流程 五、Git下载与安装 一、什么是Git 很多人都知道&#xff0c;林纳斯托瓦兹在1991年创建了开源的Linux&#xff0c;从…

鲁棒优化入门(5)—Matlab+Yalmip求解鲁棒优化编程实战

之前的博客&#xff1a;鲁棒优化入门&#xff08;二&#xff09;——基于matlabyalmip求解鲁棒优化问题 去年发布了使用Yalmip工具箱求解鲁棒优化问题的博客之后&#xff0c;陆陆续续有朋友问我相关的问题&#xff0c;有人形容从学习这篇博客到求解论文中的鲁棒优化问题&#x…

【云原生】Docker基本原理及镜像管理

目录 一、Docker概述 1.1 IT架构的演进&#xff1a; 1.2 Docker初始 1.3 容器的特点 1.4 Docker容器与虚拟机的区别 1.5 容器在内核中支持2种重要技术 1.6 Docker核心概念 1&#xff09;镜像 2&#xff09;容器 3&#xff09;仓库 二、安装Docker 2.1 Yum安装Docker…

PCTA 认证考试高分通过经验分享

作者&#xff1a; msx-yzu 原文来源&#xff1a; https://tidb.net/blog/0b343c9f 序言 我在2023年8月10日&#xff0c;参加了 PingCAP 认证 TiDB 数据库专员 V6 考试 &#xff0c;并以 90分 的成绩通过考试。 考试总分是100分&#xff0c;超过60分就算通过考试。试卷…

centos 7.9 部署django项目

1、部署框架 主要组件&#xff1a;nginx、uwsgi、django项目 访问页面流程&#xff1a;nginx---》uwsgi---》django---》uwsgi---》nginx 2、部署过程 操作系统&#xff1a;centos 7.9 配置信息&#xff1a;4核4G 50G 内网 eip &#xff1a;10.241.103.216 部署过程&…

小素数,大智慧

小素数&#xff0c;大智慧 定义判断方法方法1方法2方法3方法4方法5方法6方法7 定义 素数&#xff08;质数&#xff09;&#xff1a;在大于 1 的自然数中&#xff0c;只有 1 和该数本身两个因数的数 素数&#xff08;质数&#xff09;&#xff1a;在大于1的自然数中&#xff0c;…

K8S用户管理体系介绍

1 K8S账户体系介绍 在k8s中&#xff0c;有两类用户&#xff0c;service account和user&#xff0c;我们可以通过创建role或clusterrole&#xff0c;再将账户和role或clusterrole进行绑定来给账号赋予权限&#xff0c;实现权限控制&#xff0c;两类账户的作用如下。 server acc…

ListNode相关

目录 2. 链表相关题目 2.1 合并两个有序链表&#xff08;简单&#xff09;&#xff1a;递归 2.2 删除排序链表中的重复元素&#xff08;简单&#xff09;&#xff1a;一次遍历 2.3 两链表相加&#xff08;中等&#xff09;&#xff1a;递归 2.4 删除链表倒数第N个节点&…

ARM--day4(电灯实验、分析RCC、GPIO控制器,PMOS管、NMOS管的基本原理)

电灯实验代码&#xff1a; .text .global _start _start: /**********LED1点灯**************/RCC_INIT:1.使能GPIOE组控制器&#xff0c;通过RCC_AHB4ENSETR寄存器设置第&#xff3b;5:4&#xff3d;位写&#xff11;---->0x50000A28[4]1ldr r0,0x50000A28ldr r1,[r0]orr…

京东门详一码多端探索与实践 | 京东云技术团队

本文主要讲述京东门详业务在支撑过程中遇到的困境&#xff0c;面对问题我们在效率提升、质量保障等方向的探索和实践&#xff0c;在此将实践过程中问题解决的思路和方案与大家一起分享&#xff0c;也希望能给大家带来一些新的启发 一、背景 1.1、京东门详介绍 1.1.1、京东门…

【MySQL】索引

本期我们好好唠唠索引 目录 一、索引的概念 二、索引的重要性 三、对于索引的理解 3.1 MySQL与磁盘交互的基本单位page 3.2 MySQL中的数据交互过程 3.3 索引建立的过程 3.3.1 page的存储形式 3.3.2 B树的形成 3.4 为什么不用其他数据结构来建立索引 四、聚簇索引和非…

Python tkinter Notebook标签添加关闭按钮元素,及左侧添加存储状态提示图标案例,类似Notepad++页面

效果图展示 粉色框是当前页面&#xff0c;橙色框是鼠标经过&#xff0c;红色框是按下按钮&#xff0c;灰色按钮是其他页面的效果&#xff1b; 存储标识可以用来识别页面是否存储&#xff1a;例如当前页面已经保存用蓝色&#xff0c;未保存用红色&#xff0c;其他页面已经保存用…

【力扣】42. 接雨水 <模拟、双指针、单调栈>

【力扣】42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 目录 【力扣】42. 接雨水题解暴力双指针单调栈 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&…

数据结构<树和二叉树>顺序表存储二叉树实现堆排

✨Blog&#xff1a;&#x1f970;不会敲代码的小张:)&#x1f970; &#x1f251;推荐专栏&#xff1a;C语言&#x1f92a;、Cpp&#x1f636;‍&#x1f32b;️、数据结构初阶&#x1f480; &#x1f4bd;座右铭&#xff1a;“記住&#xff0c;每一天都是一個新的開始&#x1…

c语言实现MD5算法

MD5加密 文章目录 MD5加密MD5介绍应用场景代码分析 &#xff08;基于qt5.14.2&#xff09;测试记录 MD5介绍 1。 一种单向加密算法&#xff0c;即对明文加密&#xff0c;而不能通过密文得到明文。对原数据的任何改动&#xff0c;哪怕是1字节&#xff0c;得到的MD5值都有很大的区…