yo!这里是c++11重点新增特性介绍

目录

前言

列表初始化

{ }初始化

initializer_list类

类型推导

auto

decltype

范围for

右值引用与移动语义

左值引用和右值引用

移动语义

1.移动构造

2.移动赋值 

3.stl容器相关更新 

右值引用和万能引用

完美转发

关键字

default

delete

final和override

可变参数模板

介绍

使用场景

lambda表达式

包装器

bind函数

线程库

后记


前言

        C++11 是 C++ 语言的一个重要更新,它加入了许多新的语言特性和标准库组件,旨在提高代码的可读性、可维护性、可移植性和安全性,同时也提高了语言的表达能力和性能。C++11 的引入,对于 C++ 程序员来说是一个里程碑式的事件,它使得 C++ 语言更加现代化和高效。因此我们要作为一个重点去学习。在把本篇文章中,主要介绍一些新增特性,比如花括号初始化、initializer_list类、auto、范围for等,其中较为重要的有右值引用、lambda表达式、线程库,内容较大的特性会专门出一片文章讲解,比如智能指针、异常相关新增特性等,下面来看看上述的详细介绍吧。

列表初始化

  • { }初始化

        C++98允许使用花括号{ }对数组或者结构体元素进行统一的列表初始值设定,C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。列表初始化也可以适用于new表达式中,自定义类型不仅可以通过构造函数使用圆括号构造,也可以使用花括号构造,举例如下代码块。

class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}
private:int _year;int _month;int _day;
};
struct A
{int _a;int _aa;
};
int main()
{int a{ 0 };int arr1[] = { 1,2,3 };int arr2[] { 1,2,3 };int arr3[3] = { 0 };int arr4[3] { 0 };A a1 = { 1,2 };A a2 { 1,2 };int* ptr = new int[3]{ 1,1,1 };Date d1(1, 2, 3);Date d2{ 1,2,3 };return 0;
}
  • initializer_list类

        initializer_list类似于数组和向量,可以存储一组数据,并且支持迭代器,可以用于函数参数、构造函数和赋值运算符的参数中。通过使用initializer_list,可以轻松地传递一组数据给一个函数或者对象,而不必显式地指定这组数据的长度或者元素类型。STL中的不少容器就增加 std::initializer_list作为参数的构造函数,比如

eg:

类型推导

  • auto

        关键字auto在C++中是用于自动类型推导关键字。当使用auto声明变量时,编译器会根据变量的初始化表达式自动推导出变量的类型。使用auto可以简化代码,特别是当变量类型较长或较复杂时。另外,auto还可以结合迭代器模板等使用,更加灵活和简洁。当auto与&结合说明这是个引用变量,当auto与*结合说明是个指针变量,举例如下:

eg:

  • decltype

        decltype是一个关键字,用于获取表达式的类型,而不是用于实例化一个对象,可以用于函数返回值类型推断、模板参数类型推断等,举例:

eg:

范围for

        C++中的范围for是一种遍历容器、数组、字符串等可迭代对象的简便方法,实际底层就是迭代器遍历。范围for循环通过在循环中声明一个变量,在每次迭代中自动将其设为下一个元素的值来遍历可迭代对象中的元素。

 eg:

int arr[] = {1, 2, 3, 4, 5};
for (int x : arr) {cout << x << " ";
}
// 输出: 1 2 3 4 5

右值引用与移动语义

  • 左值引用和右值引用

        首先,无论是左值引用还是右值引用,都是给对象取别名,要弄明白左值引用和右值引用,先了解一下左值与右值是什么意思。对于左值,可以获取它的地址+对它赋值,注意左值可以出现在赋值符号的左边,也可以出现在右边,左值引用就是对左值的引用,给左值取别名;在此之前所学的引用都是左值引用,左值引用使用一个&符号来声明,比如:

	int a = 10;    //左值int* b = new int(1);    //左值const int c = 1;    //左值int& refa = a;    //左值引用int*& refb = b;    //左值引用const int& refc = c;    //左值引用

        对于右值,不能取地址+不能出现在赋值符号的左边,是一个表示数据的表达式,比如字面常量、表达式返回值等,右值引用就是对右值的引用,给右值取别名,比如: 

	int x = 0, y = 0;1;   //右值x + y;   //右值x + 1;   //右值int&& rr1 = 1;   //右值引用int&& rr2 = x + y;   //右值引用int&& rr3 = x + 1;   //右值引用//int&& ref4 = x;   //报错

左值引用与右值引用的比较:

        ①左值引用只能引用左值,不能引用右值,但是const左值引用既可引用左值,也可引用右值;

        ②右值引用只能右值,不能引用左值,但是右值引用可以move以后的左值;

其中move函数的作用就在于将左值强制转换为右值,比如:

    int a = 10;//int& d = 10;  //左值引用引用不了右值const int& d = 10;   //const左值引用可以引用右值//int&& e = a;   //右值引用引用不了左值int&& e = move(a);   //右值引用可以引用move之后的左值
  • 移动语义

1.移动构造

        那左值引用用的好好的,为什么要提出右值引用呢?我们想一下左值引用的短板,有这样一个情况,当函数返回值是一个局部变量,出了作用域就会被销毁,就不能使用(左值)引用返回,只能使用传值返回,但是传值返回至少会有一次拷贝构造(即使在编译器优化以后),因此为了减少拷贝,下面考虑其他方法——引入移动构造、移动赋值

        在此之前,先介绍一下右值的分类,包括纯右值(内置类型右值)将亡值(自定义类型右值),对于纯右值,就算是拷贝多次也无所谓,但是对于有申请资源的将亡值,拷贝一次都是极大地降低了效率,所以考虑将将亡值的资源转给需要的新对象,也就是用将亡值即将不要的资源去构造给需要的对象,这可以大大的减少拷贝,提高效率。

        通过下面一个具体的例子描述一下这个过程,在模拟实现string类时,有这样一个int转string的函数,如下图,左边是string的拷贝构造函数,右边是To_string函数传值返回的过程,正常编译器优化情况下,会将str的资源拷贝一份给main函数中的str,但是我们发现这个To_string函数中的str就是一个将亡值,退出函数str就会被释放,而main函数中的str正好是一个需要此资源的新对象,正如上面所说,将To_string函数中的str对象资源移动给main函数中的str,这样就是一次移动构造,如第二图。

        对于移动构造函数,我们可以看到,参数是一个右值引用,函数体就是进行资源的交换或者说移动,为什么To_string函数返回之后会调用移动构造函数去构造str呢?因为这里编译器会将To_string函数的局部变量返回值识别成一个右值(将亡值),就会自动去找最匹配的构造函数去构造对象。当没有移动构造函数时,这里就会去调用拷贝构造函数,因为const左值引用也可以接收一个右值,当两个都存在时,存在构造对象的地方会去调用最匹配的构造函数。

2.移动赋值 

        不仅有移动构造,还有移动赋值,原理一样,这里也简单说一下,结合在一起较为容易理解。如下图,对于移动赋值函数,参数依旧是右值引用,函数体内也是在不是两个相同的对象赋值的情况以外,交换或移动将亡值的资源给目标对象,实现资源的转移以提高效率。

3.stl容器相关更新 

         移动构造和移动赋值不仅解决了传值返回的多次拷贝问题,而且这种资源移动的思想也应用到了stl的容器上,为相关接口增加了右值引用版本,以减少对象的拷贝,如:

eg:

        同时,在原本6个默认成员函数的基础上又增加了两个默认成员函数——移动构造函数和移动赋值运算符重载。注意:

①如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。那么编译器会自动生成一个默认移动构造;

②如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中 的任意一个,那么编译器会自动生成一个默认移动赋值,

        实际上,实现一个类有申请资源时,则得实现拷贝构造、析构、拷贝赋值以进行深拷贝,同时想减少拷贝,就得实现移动构造和移动赋值,但由于有了上面那三个,编译器就不会自动生成,所以还是得自己实现这两个,因此存在属性申请资源时,自己实现拷贝构造、析构、拷贝赋值、移动构造、移动赋值。 

  • 右值引用和万能引用

        万能引用主要有两种,一种是在函数模板中使用的一种引用类型,它的语法形式为“T&&”,其中T是一个模板参数。还有一种是“auto&&”,万能引用可以接受任意类型的实参,并且保留了实参的左右值属性。值得注意的是,必须存在类型推导才是万能引用,否则是右值引用。举例如下图,特别要注意最后一个例子,其中push_back函数得参数虽然是T&&,但是在模板实例化时T的理性就已确定,不存在类型推导,而且在前面也提到过,这个是容器新增得右值引用版本接口,不是万能引用。

eg:

  • 完美转发

        完美转发提供了一种机制来保留函数参数的完整类型信息,并将其转发给另一个函数。传统上,在C++中,当一个函数接收一个参数并将其转发给另一个函数时,它会失去原始参数的类型信息(比如说右值引用版本的接口接收一个右值引用,但是在函数体内这个变量被当作左值去使用,那当我们需要去使用它的右值特性去调用其他相关函数时就没有办法了),此时C++完美转发保留了它的左值或右值的属性。语法如下:

template<typename T>
void func(T&& arg)
{other_func(std::forward<T>(arg));   //完美转发
}

        下面通过一个例子来展现一下完美转发的使用场景,如下代码是List类的模拟实现,仅包括尾插和插入函数,在mian函数中,尾插一个“1111”的常量字符串,毫无疑问,会匹配右值引用版本的push_back函数,其中需要复用insert函数,而且需要复用右值引用版本的insert函数,但是在push_back函数的函数体内,x已经被当作成了左值,已经失去了“1111”的右值特性,此时使用万能转发保持其属性,继续会匹配右值引用版本的insert函数,在这个函数体内,也需要去调用右值引用版本的Node节点的构造函数,也就是移动构造函数,也必须通过万能引用去操作,在Node的移动构造函数中,我们也需要将右值版本的字符串放进_data中,也是通过万能转发的方法。

        从上面的例子当中可以看出,万能转发在实际开发中也是较为需要的,较为重要的。

代码:

template <class T>
struct ListNode
{//构造函数用来创节点ListNode(const T& x = T())   //左值版本:_data(x), _prev(nullptr), _next(nullptr){}ListNode(T&& x)   //右值版本:_data(forward<T>(x)), _prev(nullptr), _next(nullptr){}T _data;ListNode<T>* _prev;ListNode<T>* _next;
};template <class T>
class List
{typedef ListNode<T> Lnode;
public://...iterator insert(iterator pos, const T& x)   //左值版本{Lnode* newNode = new Lnode(x);pos._node->_prev->_next = newNode;newNode->_prev = pos._node->_prev;newNode->_next = pos._node;pos._node->_prev = newNode;return iterator(newNode);  //返回插入位置的迭代器}iterator insert(iterator pos, T&& x)   //右值版本{Lnode* newNode = new Lnode(forward<T>(x));pos._node->_prev->_next = newNode;newNode->_prev = pos._node->_prev;newNode->_next = pos._node;pos._node->_prev = newNode;return iterator(newNode);  //返回插入位置的迭代器}void push_back(const T& x)   //左值版本{insert(end(), x);}void push_back(T&& x)   //右值版本{insert(end(), forward<T>(x));}
private:Lnode* _head;
};int main()
{List<string> lt;lt.push_back("1111");return 0;
}

关键字

  • default

        关键字default用于强制生成默认函数,可以更好的控制默认函数。比如,当只有拷贝构造函数时,运行会报错没有默认构造函数,此时使用default强制自动生成即可,如下图

eg:

 

  • delete

        关键字delete用于禁止生成默认函数,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数

eg:

  • final和override

        find和override关键字在之前的章节继承和多态中讲过,对于final,即可以修饰类不能被继承,也可以修饰虚函数不能被重写;对于override,放在子类中,检查子类虚函数是否重写了父类的虚函数,具体可见http://t.csdnimg.cn/5CvsAicon-default.png?t=N7T8http://t.csdnimg.cn/5CvsA

可变参数模板

  • 介绍

        可变参数模板可以让我们编写接受可变数量参数类型的函数和类模板。下面是一个基本可变参数的函数模板,args前面有省略号,称为参数包,其中包含若干个模板参数,我们无法直接获取其中的每个参数,只能展开参数包的方式获取,在C++中,有两种方式展开可变参数模板的参数包:递归函数方式展开和逗号表达式方式展开

template <typename... Args>
void printArgs(Args... args)
{}

递归函数方式展开:

         递归展开是指在函数或类模板中递归调用自己,并将参数包展开为独立的参数列表。这可以通过使用递归模板函数或类模板来实现。如下代码,包括递归终止函数和普通展开函数,main函数中的ShowList调用过程为:

①1传进t,其余初步传进args参数包,继续递归调用展开函数;

②'a'传进t,其余传进args参数包,此时参数包只剩一个参数"111"了;

③调用最匹配的函数,即递归终止函数,传进t,之后递归结束,每个参数也最终获取到了。

template <class T>
void ShowList(const T& t)   //递归终止函数
{cout << t << endl;
}template <class T, class ...Args>
void ShowList(const T& t, Args... args)   //展开函数
{cout << t << endl;   //t就是参数包里的一个参数,这里进行使用即可ShowList(args...);
}int main()
{ShowList(1, 'a', "111");
}

逗号表达式方式展开: 

        利用初始化列表来初始化一个变长数组,{(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... ),最终会创建一个元素值都为0的数组arr,由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args) 获取到当前的参数,也就是说在构造int数组的过程中就将参数包展开了,因此获取到参数包中的所有参数,注意这个数组的目的纯粹是为了在数组构造的过程展开参数包,使用参数的地方是在PrintArg函数中。

template <class T>
void PrintArg(T t)
{cout << t << " ";
}//展开函数
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}int main()
{ShowList(1, 'A', "111");return 0;
}
  • 使用场景

        如果上面的参数包、展开方式你并没有看懂,那就作为了解即可,但是使用场景必须能看得懂,可变参数模板应用在stl容器的emplace相关接口上,比如

        可以看到emplace接口参数,既支持模板的可变参数,又是万能引用,也就是同时可以接受左值,也可以接受右值,下面看看如何使用这个接口:其中对于一个元素是pair类的vector,可以直接将一个pair的元素使用emplace_back插入,但是push_back的话就必须去调用make_pair函数。

int main()
{vector<pair<string, int>> v;v.emplace_back("1", 1);//v.push_back("1", 1);   //报错v.push_back(make_pair("1", 1));v.push_back({ "1", 1 });return 0;
}

lambda表达式

        lambda表达式是一种匿名函数,可以在需要函数对象的任何地方使用。lambda表达式的基本语法如下:

[capture-list] (parameters) mutable -> return-type { function-body }

        其中,

捕获列表(capture-list):用于捕获外部变量,该列表总是出现在lambda函数的开始位置,编译器根据[ ]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda 函数使用,每个变量可以指定为按值捕获或按引用捕获,

  • [var]:表示值传递方式捕捉变量var,正常情况下可读不可写,加上mutable变成了一份拷贝,就可读可写了
  • [=]:表示值传递方式捕获所有所在栈帧的变量(包括this)
  • [&var]:表示引用传递捕捉变量var
  • [&]:表示引用传递捕捉所有所在栈帧的变量(包括this)
  • 由多个捕捉项组成,并以逗号分割

eg:

        [=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量;

        [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量,

参数(parameters):用于传递参数,与普通函数的参数列表一致,如果不需要参数传递,则可以 连同()一起省略;

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。注意使用该修饰符时,参数列表不可省略(即使参数为空);

返回类型(return-type):用于指定返回值类型,没有返回值时此部分可省略。返回值类型明确情况下也可省略,由编译器对返回类型进行推导;

函数体(function-body):用于实现函数的具体逻辑,可以使用捕获列表的变量也可以使用参数列表的变量,

        如下图,fun2就是一个lambda表达式,值传递方式捕获了上文的所有变量,其中b是引用传递,传了一个参数c,返回值是int,这里不写也没事,因为编译器会自动推导,函数体内运算以后返回b,之后调用此lambda表达式,需要传一个参数,即可得到函数体内的计算结果。

注意:

        ①捕捉列表不允许变量重复传递,否则就会导致编译错误,比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复,就会报错;

        ②lambda表达式之间不能相互赋值,但可以拷贝构造一个lambda表达式,也可以赋值给相同类型的函数指针,比如

void (*PF)();
int main()
{auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };//f1 = f2;   //报错auto f3(f1);PF = f2;return 0;
}

包装器

        包装器,也叫适配器,是一种用于以统一的方式调用不同类型函数的抽象概念,本质是一个类模板。在引入lambda表达式之后,有没有这样一个问题,有的接口用函数实现,有的用函数对象实现,还有的用lambda表达式实现,万一有场景需要把这些不同实现方式的接口聚合在一起,该用什么来接收这些接口呢?对!就是使用包装器去接收,看看它的原型:

template <class T> function;
template <class Ret, class... Args>
class function<Ret(Args...)>;

其中,Ret: 被调用函数的返回类型,Args…:被调用函数的形参,使用方式我举个例子,实现计算器的加减乘除功能,注意实现以及调用的细节。

int Add(int a, int b)
{return a + b;
}class Sub
{
public:int operator()(int a, int b){return a-b;}
};class func
{
public:int Div(int a, int b){return a / b;}
};int main()
{function<int(int, int)> ADD = Add;   //函数名function<int(int, int)> SUB = Sub();   //函数对象function<int(func, int, int)> DIV = &func::Div;   //非静态成员函数function<int(int, int)> MUL = [](int a, int b) {return a * b; };   //lambda表达式cout << ADD(1, 2) << endl;cout << SUB(1, 2) << endl;cout << DIV(func(), 1, 2) << endl;cout << MUL(1, 2) << endl;return 0;
}

运行: 

bind函数

        bind函数是一个非常强大的函数对象适配器,它可以把一个函数和一些参数绑定起来,形成一个新的函数对象,该函数对象可以像原函数一样调用,但是它已经部分确定了原函数的参数,同时还可以实现参数顺序调整,原型如下:

template <typename F, typename... Args>
auto bind(F&& f, Args&&... args);

先看把普通函数和成员函数的一些参数绑定的例子:

int Add(int a, int b)
{return a + b;
}class Func
{
public:int Mul(int a, int b){return a * b;}
};int main()
{//有了两数相加函数,实现任意数加7的功能auto xPlus7 = bind(Add, placeholders::_1, 7);cout << xPlus7(1) << endl;//有了两数相乘的成员函数,实现任意数加倍的功能auto increaseDouble = bind(&Func::Mul, Func(), placeholders::_1, 2);cout << increaseDouble(8) << endl;return 0;
}

运行:

再看调整参数的例子:

double Div(int a, int b)
{return (double)a / b;
}int main()
{auto Divide1 = bind(Div, placeholders::_1, placeholders::_2);auto Divide2 = bind(Div, placeholders::_2, placeholders::_1);cout << Divide1(8, 2) << endl;cout << Divide2(8, 2) << endl;return 0;
}

运行:

线程库

        学了之后再补充...

后记

        从以上可以看出,c++11新增的知识点还是特别多的,本文章只是讲述了较为重要的一部分,面试时被提问频率高的一部分,还有一部分没有提到,比如新增容器(如array),空指针nullptr,有一些大家可能已经熟练于心了,对于文中讲过的知识点,其中包括范围for、右值引用、lambda表达式都是重点中的重点,希望大家能够真正的看懂并理解,拜拜!


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

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

相关文章

西米支付:简单介绍一下支付公司的分账功能体系

随着互联网的普及和电子商务的快速发展&#xff0c;支付已经成为人们日常生活的重要组成部分。支付公司作为第三方支付平台&#xff0c;为消费者和商家提供了便捷、安全的支付方式。而在支付领域中&#xff0c;分账功能是一个非常重要的功能&#xff0c;它可以帮助企业实现资金…

C语言——从终端(键盘)将 5 个整数输入到数组 a 中,然后将 a 逆序复制到数组 b 中,并输出 b 中 各元素的值。

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int i;int a[5];int b[5];printf("输入5个整数&#xff1a;\n");for(i0;i<5;i){scanf("%d",&a[i]);}printf("数组b的元素值为&#xff1a;\n");for(i4;i>0;i--…

Javascript每天一道算法题(十五)——轮转数组_中等(一行解决轮转数组)

文章目录 1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——while遍历&#xff08;较为复杂&#xff0c;不推荐&#xff09;&#xff08;2&#xff09;方法2&#xff08;直接截取后插入&#xff0c;推荐&#xff09;&#xff08;3&#xff09;方法3——优化方法2&a…

jQuery_03 dom对象和jQuery对象的互相转换

dom对象和jQuery对象 dom对象 jQuery对象 在一个文件中同时存在两种对象 dom对象: 通过js中的document对象获取的对象 或者创建的对象 jQuery对象: 通过jQuery中的函数获取的对象。 为什么使用dom或jQuery对象呢&#xff1f; 目的是 要使用dom对象的函数或者属性 以及呢 要…

python -opencv 轮廓检测(多边形,外接矩形,外接圆)

python -opencv 轮廓检测(多边形&#xff0c;外接矩形&#xff0c;外接圆) 边缘检测步骤: 第一步&#xff1a;读取图像为灰度图 第二步&#xff1a;进行二值化处理 第三步&#xff1a;使用cv2.findContours对二值化图像提取轮廓 第三步&#xff1a;将轮廓绘制到图中 代码如下…

Hibernate的三种状态

1.瞬时状态(Transient) 通过new创建对象后&#xff0c;对象并没有立刻持久化&#xff0c;他并未对数据库中的数据有任何的关联&#xff0c;此时java对象的状态为瞬时状态&#xff0c;Session对于瞬时状态的java对象是一无所知的&#xff0c;当对象不再被其他对象引用时&#xf…

【TL431+场效应管组成过压保护电路】2022-3-22

缘由这个稳压三极管是构成的电路是起到保护的作用吗&#xff1f;-硬件开发-CSDN问答

HTML5+ API 爬坑记录

背景: 有个比较早些使用5开发的项目, 最近两天反馈了一些问题, 解决过程在此记录; 坑1: plus.gallery.pick 选择图片没有进入回调 HTML5 API Reference 在 联想小新 平板电脑上选择相册图片进行上传时, 打开相册瞬间 应用会自动重启, 相册倒是有打开, 不过应用重启了, 导…

[原创](免改BIOS)使用Clover升级旧电脑-(高阶玩法)让固态硬盘内置Win11 PE启动系统

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…

【算法专题】滑动窗口—无重复字符的最长子串

力扣题目链接&#xff1a;无重复字符的最长子串 一、题目解析 二、算法原理 解法一&#xff1a;暴力解法&#xff08;时间复杂度最坏&#xff1a;O(N)&#xff09; 从每一个位置开始往后枚举&#xff0c;在往后寻找无重复最长子串时&#xff0c;可以利用哈希表来统计字符出现…

手机APP-MCP走蓝牙无线遥控智能安全帽~执法记录仪~拍照录像,并可做基础的配置,例如修改服务器IP以及配置WiFi等

手机APP-MCP走蓝牙无线遥控智能安全帽~执法记录仪~拍照录像,并可做基础的配置,例如修改服务器IP以及配置WiFi等 手机APP-MCP走蓝牙无线遥控智能安全帽~执法记录仪~拍照录像,并可做基础的配置,例如修改服务器IP以及配置WiFi等&#xff0c; AIoT万物智联&#xff0c;智能安全帽…

JVM之jvisualvm多合一故障处理工具

jvisualvm多合一故障处理工具 1、visualvm介绍 VisualVM是一款免费的&#xff0c;集成了多个 JDK 命令行工具的可视化工具&#xff0c;它能为您提供强大的分析能力&#xff0c;对 Java 应 用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回…

SpringBoot:异步任务基础与源码剖析

官网文档&#xff1a;How To Do Async in Spring | Baeldung。 Async注解 Spring框架基于Async注解提供了对异步执行流程的支持。 最简单的例子是&#xff1a;使用Async注解修饰一个方法&#xff0c;那么这个方法将在一个单独的线程中被执行&#xff0c;即&#xff1a;从同步执…

系列六、Spring整合单元测试

一、概述 Spring中获取bean最常见的方式是通过ClassPathXmlApplicationContext 或者 AnnotationConfigApplicationContext的getBean()方式获取bean&#xff0c;那么在Spring中如何像在SpringBoot中直接一个类上添加个SpringBootTest注解&#xff0c;即可在类中注入自己想要测试…

java反序列化漏洞详解

java反序列化漏洞 文章目录 java反序列化漏洞漏洞原理漏洞评级漏洞危害漏洞验证漏洞防御典型案例 漏洞原理 由于java开发人员在编写代码时重写了 readObject 方法&#xff0c;在重写的 readObject 方法中调用其他函数实现链式调用最终调用到了危险函数&#xff0c;从而形成反序…

【C++】泛型编程 ⑮ ( 类模板示例 - 数组类模板 | 自定义类中持有指针成员变量 )

文章目录 一、支持 数组类模板 存储的 自定义类1、可拷贝和可打印的自定义类2、改进方向3、改进方向 - 构造函数4、改进方向 - 析构函数5、改进方向 - 重载左移运算符6、改进方向 - 重载拷贝构造函数 和 等号运算符 二、代码示例1、Array.h 头文件2、Array.cpp 代码文件3、Test…

[网鼎杯 2020 朱雀组]phpweb

看一下源码 应该是输入的date 作为函数&#xff0c;value作为内部参数的值&#xff0c;将date()函数返回的结果显示在页面上 回去看的时候&#xff0c;意外发现页面有了新的跳转&#xff0c;观察一下发现&#xff0c;页面每隔五秒就会发生一次跳转 所以就抓包看看 抓包发现po…

GEE:kNN(k-最近邻)分类教程(样本制作、特征添加、训练、精度、最优参数、统计面积)

作者:CSDN @ _养乐多_ 本文将介绍在Google Earth Engine (GEE)平台上进行kNN(k-最近邻)分类的方法和代码,其中包括制作样本点教程(本地、在线和本地在线混合制作样本点,合并样本点等),加入特征变量(各种指数、纹理特征、时间序列特征、物候特征等),运行kNN(k-最近…

Linux中,查看Tomcat版本、如何查看Tomcat版本

方法 在tomcat的bin目录下&#xff0c;执行version.sh命令即可 结果

ElementUI table+dialog实现一个简单的可编辑的表格

table组件如何实现可编辑呢&#xff1f; 我的需求是把table组件那样的表格&#xff0c;实现它点击可以弹出一个框&#xff0c;然后在这个框里面输入你的东西&#xff0c;然后将他回显回去&#xff0c;当然&#xff0c;输入的有可能是时间啥的。 为什么要弹出弹层不在框上直接…