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表达式都是重点中的重点,希望大家能够真正的看懂并理解,拜拜!


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数组题目: 665. 非递减数列、453. 最小移动次数使数组元素相等、283. 移动零、189. 旋转数组、396. 旋转函数

665. 非递减数列 题解&#xff1a; 题目要求一个非递减数列&#xff0c;我们可以考虑需要更改的情况&#xff1a; nums {4, 2, 5} 对于这个nums&#xff0c;由于2的出现导致非递减&#xff0c;更改的情况就是要么4调到<2&#xff0c;要么2调到4,5. nums {1, 4, 2, 5} …

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…

局域网协议:VLAN技术介绍

文章目录 VLAN概述VLAN的优点VLAN的原理VLAN的配置推荐阅读 VLAN概述 VLAN&#xff08;Virtual Local Area Network虚拟局域网&#xff09;是一种在物理网络基础上划分逻辑上独立的局域网的技术。它允许将网络设备按照逻辑上的需求而非物理位置进行分组&#xff0c;提供更好的…

@RequestMapping

目录 作用&#xff1a; 位置&#xff1a; 属性 1.value 2.method 3.params 4.header 作用&#xff1a; 该注解是一个用来处理请求地址映射的注解。 位置&#xff1a; 可用于映射一个请求或一个方法&#xff0c;可以用在类或方法上。 用于方法上&#xff0c;表示在类的…

OSG粒子系统与阴影-雾效模拟(1)

虚拟现实中有很多效果&#xff0c;如雨效、雪效、雾效等&#xff0c;这些都可以通过粒子系统来实现。一个真实的粒子系统的模式能使三维场景达到更好的效果。 本章对OSG粒子系统的使用以及生成自定义粒子系统的方法进行了详细介绍最后还附带说明了阴影的使用方法。在实时的场景…

html幸运大转盘抽奖(附源码)

文章目录 1.设计来源1.1 幸运大转盘 风格11.2 幸运大转盘 风格21.3 幸运大转盘 风格31.4 幸运大转盘 奖品效果1.5 幸运大转盘 活动未开始1.6 幸运大转盘 活动已结束1.7 幸运大转盘 图片源素材 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&a…

毅速丨3D打印随形水路为何受到模具制造追捧

在模具制造行业中&#xff0c;随形水路镶件正逐渐成为一种革命性的技术&#xff0c;其提高冷却效率、优化产品设计、降低成本等优点&#xff0c;为模具制造带来了巨大的创新价值。 随形水路是一种根据产品形状定制的冷却水路&#xff0c;其镶件可以均匀地分布在模具的表面或内部…

指针运算详解

1.引入 指针的基本运算有三种&#xff0c;分别是&#xff1a; • 指针- 整数 • 指针-指针 • 指针的关系运算 2.指针- 整数 因为数组在内存中是连续存放的&#xff0c;只要知道第⼀个元素的地址&#xff0c;顺藤摸⽠就能找到后⾯的所有元素。 int arr[10] {1,2,3,4,5,…

Nginx安装与配置、使用Nginx负载均衡及动静分离、后台服务部署、环境准备、系统拓扑图

目录 1. 系统拓扑图 2. 环境准备 3. 服务器安装 3.1 mysql&#xff0c;tomcat 3.2 Nginx的安装 4. 部署 4.1 后台服务部署 4.2 Nginx配置负载均衡及静态资源部署 1. 系统拓扑图 说明&#xff1a; 用户请求达到Nginx若请求资源为静态资源&#xff0c;则将请求转发至静态…

HarmonyOS开发:ArkTs常见数据类型

前言 无论是Android还是iOS开发&#xff0c;都提供了多种数据类型用于常见的业务开发&#xff0c;但在ArkTs中&#xff0c;数据类型就大有不同&#xff0c;比如int&#xff0c;float&#xff0c;double&#xff0c;long统一就是number类型&#xff0c;当然了也不存在char类型&…

【C/PTA —— 10.函数1(课外实践)】

C/PTA —— 10.函数1&#xff08;课外实践&#xff09; 一.函数题6-1 符号函数6-2 求排列数6-3 求一个大于10的n位整数w的后n-1位的数&#xff0c;并作为函数值返回。6-4 其右上三角&#xff08;含主对角线&#xff09;元素之和。6-5 字符串比较6-6 使用函数求素数和6-7 使用函…

简答的体系架构分析

背景 一点体系架构的分析 体系架构图

常用数据存储格式介绍:Excel、CSV、JSON、XML

在现代数字时代&#xff0c;数据经过提炼后可以推动创新、简化运营并支持决策流程。然而&#xff0c;在提取数据之后&#xff0c;并将其加载到数据库或数据仓库之前&#xff0c;需要将数据转化为可用的数据存储格式。本文将介绍开发者常用的4种数据存储格式&#xff0c;包括 Ex…

待办委托超方便,流程审批效率大提升丨三叠云

流程委托 路径 我的流程 >> 我的待办 功能简介 我的流程增加「待办委托」功能&#xff0c;用户可以将待处理的流程审批委托他人处理。 应用场景&#xff1a; 如果当前审批人不方便审批时&#xff0c;可以委托给指定的人&#xff08;被委托人&#xff09;处理&#…

怎样自动把网页截图发到微信群里

现在很多公司都在使用企业微信了&#xff0c;不但方便公司内部交流和客户交流&#xff0c;还能组建各种小组群&#xff0c;业务群。企业微信群提供一个机器人的功能&#xff0c;方便我们把公司业务信息&#xff0c;或来自外部的信息自动发布到群里。 这里研究一下如何向微信群…

DQN算法

DQN算法 教程链接 DataWhale强化学习课程JoyRL https://johnjim0816.com/joyrl-book/#/ch7/main DQN算法 DQN(Deep Q-Network) 主要创新点在于将Q-learning算法中的Q表记录动作价值函数转为引入深度神经网络来近似动作价值函数 Q ( s , a ) Q(s,a) Q(s,a),从而能够处理连续…

智慧城市运营管理平台解决方案:PPT全文61页,附下载

关键词&#xff1a;智慧城市建设方案&#xff0c;智慧城市解决方案&#xff0c;智慧城市的发展前景和趋势&#xff0c;智慧城市建设内容&#xff0c;智慧城市运营管理平台 一、智慧城市运营平台建设背景 随着城市化进程的加速&#xff0c;城市面临着诸多挑战&#xff0c;如环…

Keil5MDK创建C51工程

Keil5MDK创建C51工程 1.概述 上篇文章介绍了安装Keil5MDK和C51工具&#xff0c;这篇文章介绍工具的使用&#xff0c;首先介绍如何创建一个51单片机工程&#xff0c;写一个demo程序通过编译&#xff0c;烧录到单片机。 第一篇安装工具文章地址&#xff1a;https://blog.csdn.ne…

智慧楼宇可视化视频综合管理系统,助力楼宇高效安全运行

随着互联网技术的进步和发展&#xff0c;智能化的楼宇建设也逐步成为人们选择办公场所是否方便的一个重要衡量因素。在智能化楼宇中&#xff0c;安全管理也是重要的一个模块。得益于互联网新兴技术的进步&#xff0c;安防视频监控技术也得到了快速发展并应用在楼宇的安全管理中…

区块链技术与应用 【全国职业院校技能大赛国赛题目解析】第四套区块链应用后端开发

第四套区块链应用后端开发 环境 : ubuntu20 fisco : 2.8.0 springboot 2.1.1 fisco-java-sdk: 2.7.2 maven 3.8.8 前言 这套后端样题,只涉及调用fisco的系统接口,不涉及此食品溯源项目的业务接口,所以我就直接生成一个springboot项目进行完成此题目。 请提前准备好一…