31 C++11

news2024/12/23 14:22:58

本节目标

  1. c++11简介
  2. 列表初始化
  3. 变量类型推导
  4. 范围for循环
  5. 新增加容器
  6. 右值
  7. 新的类功能
  8. 可变参数模板

1. c++11简介

在2003年标准委员会提交了一份计数勘误表(简称TC1),使得c++03这个名字已经取代了c++98称为c++11之前的最新的c++标准名称。不过由于c++03(TC1)主要是对c++98标准中的漏洞进行修复,语言的核心部分没有改动,因此人们习惯性的把两个标准合并称为c++98/03标准。从c++0x到c++11,c++标准10年磨一剑,第二个真正意义上的标准姗姗来迟。相对于c++98/03,c++11则带来了数量可观的变化,其中包含了约140个新特性,以及对c++03标准中约600个缺陷的修正,这使得c++11更像是从c++98中孕育出来的新语言。相比较而言,c++11能更好的用于系统开发和库开发、语法更加泛化和简单化,更稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用的比较多,所以要作为一个重点去学习。c++11增加的语法特性篇幅很多,没办法一一讲解
https://en.cppreference.com/w/cpp/11

小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际标准委员会在研究C++03的下一个版本的时候,一开始计划2007年发布,所以最初这个标准叫C++07。但是到06年的时候,官方觉得2007年肯定完不成C++07,而且官方觉得2008年可能也完不成。最后干脆叫C++0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11

2. 统一的列表初始化

2.1 {} 初始化

在c++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如

struct Point
{
	int _x;
	int _y;
};

int main()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[5] = { 0 };
	Point p = { 1, 2 };
	return 0;
}

c++11扩大了用大大括号的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义类型,使用初始化列表时,可添加等号(=),也可不添加

int x1 = 1;
int x2{ 2 };

//new也可以列表初始化
int* pa = new int[4] {0};

创建对象u额可以用列表初始化方式调用构造函数初始化,但有本质区别,是先用括号的内容构造一个临时对象,再拷贝构造给初始化对象,会优化为直接构造

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;
};

//创建对象列表初始化
Date d1( 2022, 1 ,1 ); //旧初始化方式
Date d2{ 2022, 1, 2 };
Date d3 = { 2022, 1, 3 };

//单参数构造函数支持隐士类型转换
string str = "xxxxx";

//证明,这里不能优化,所以转不了,必须加const
const Date& d4 = { 2023, 11 , 5 };

2.2 std::initializer_list

介绍文档
http://www.cplusplus.com/reference/initializer_list/initializer_list/

是什么类型:

 // the type of il is an initializer_list 
	auto il = { 10, 20, 30 };
    cout << typeid(il).name() << endl;

在这里插入图片描述

Date d1( 2022, 1 ,1 );
vector<int> v1 = { 0, 1, 2, 3, 4 };

可以使用迭代器遍历

initializer_list<int> l2 = { 0, 1, 2, 3, 4 };
initializer_list<int>::iterator it = l2.begin();
while (it != l2.end())
{
	cout << *it << " ";
	it++;
}

在这里插入图片描述

这里的v1和d1不一样,上面的是构造的对象,下面是先构造的initializer_list类型,v1的参数个数可以是随意的,d1只能是三个

使用场景
std::initializer_list一般是作为构造函数的参数,c++11对stl中的不少容器增加了它作为参数的构造函数,这样初始化容器就方便多了。也可以作为operator=的参数,就可以用大括号赋值

在这里插入图片描述
让vector也支持{}初始化和赋值

vector(std::initializer_list<T> lt)
{
	Reserve(lt.size());
	for (auto& e : lt)
	{
		PushBack(e);
	}
}
vector<T>& operator=(initializer_list<T> l) {
 vector<T> tmp(l);
 std::swap(_start, tmp._start);
 std::swap(_finish, tmp._finish);
 std::swap(_endofstorage, tmp._endofstorage);
 return *this;
 }

当参数的个数和构造函数匹配时会识别为对象,不匹配时会认为是initializer_list类型

3. 声明

c++11通了多种简化声明的方式,尤其是在使用模板时

3.1 auto

c++98中auto是一个存储类型的说明符,表明变了是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。c++11废除auto原来用法,将其用于实现自动类型判断,这样要求必须显示初始化,让比那一期将定义对象的类型设置为初始化值的类型
typeid可以获得变量的类型字符串

int i = 10;
auto p = i;
auto pf = strcpy;

cout << typeid(p).name() << endl;
cout << typeid(pf).name() << endl;

在这里插入图片描述

3.2 decltype

上面可以推导类型,但推导的类型不能用来创建变量,如果想根据某个变量类型推导并创建变量,可以用decltype,将变量的类型声明为表达式指定的类型

double y = 2.2;
decltype(y) ret = 3.3;

cout << typeid(ret).name() << endl;

3.3 nullptr

由于c++中NULL被定义为字面量0,这样就可能带来一些问题,0既指针常量,又表示整形常量。所以出于清晰和安全的角度考虑,c++11新增了nullptr,表示空指针

#ifndef NULL
 #ifdef __cplusplus
 #define NULL    0
 #else
 #define NULL    ((void *)0)
 #endif
 #endif

4.范围for循环

5. stl一些变化

圈起来的是几个新容器,但是实际最有用的是unordered_map和unordered_set。
在这里插入图片描述
array和内置数组相比,越界访问会报错
容器的新方法
增加的新方法都用的比较少。比如提供了cbegin和cedn方法返回const迭代器等待,但意义不大,begin和end也可以返回const迭代器,属于锦上添花的操作
插入接口函数增加了右值版本
http://www.cplusplus.com/reference/vector/vector/emplace_back/
意义在哪,说能提高效率,如何提高的

在这里插入图片描述

6. 右值引用和移动语义

6.1 左值引用和右值引用

传统c++语法就有引用,c++11新增了右值引用特性,无论是左值还是右值引用,都是给对象取别名

左值是一个表示数据的表达式(变量名或解引用的指针),可以获取它的地址,可以赋值,左值可以出现在赋值符号左边,右值不能出现在赋值符号左边。定义时const修饰后的左值,不能赋值,但可以取地址。左值引用就是给左值的引用,取别名

int* p = new int(0);
int b = 1;
const int c = 2;

//以下都是左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

什么是右值,什么是右值引用
右值也是一个表示数据的表达式,如:字面常量、表达式返回值、函数返回值(这个不能是左值引用返回)等待,右值可以出现在赋值符号的右边,不能出现在左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名

double x = 1.1, y = 2.2;
//以下是常见的右值
10;
x + y;
fmin(x, y);

//以下几个都是右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);

//编译会报错,error c2106, "=":左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;

6.2 左值引用和右值引用比较

左值引用总结:
1.左值引用只能引用左值,不能引用右值,但是const左值引用既可以引用左值,也可以引用右值
2.右值引用不能引用左值,move的可以

//左值引用不能给右值取别名,const左值可以
int& r1 = 10;
const int& r2 = 10;
//右值引用不能给左值取别名,move可以
int i = 10;
int&& rr3 = i;
int&& rr4 = move(i);

6.3 左值引用使用场景和意义

左值做参和返回值都可以提高效率,减少了拷贝

#pragma once
#include <string>
#include <iostream>
#include <assert.h>

class string
{
public:
	typedef char* iterator;
	iterator begin()
	{
		return _str;
	}
	iterator end()
	{
		return _str + _size;
	}
	string(const char* str = "")
		:_size(strlen(str))
		, _capacity(_size)
	{
		//cout << "string(char* str)" << endl;
		_str = new char[_capacity + 1];
		strcpy(_str, str);
	}
	// s1.swap(s2)
	void swap(string& s)
	{
		::std::swap(_str, s._str);
		::std::swap(_size, s._size);
		::std::swap(_capacity, s._capacity);
	}
	// 拷贝构造
	string(const string& s)
		:_str(nullptr)
	{
		std::cout << "string(const string& s) -- 深拷贝" << std::endl;
		string tmp(s._str);
		swap(tmp);
	}
	// 赋值重载
	string& operator=(const string& s)
	{
		std::cout << "string& operator=(string s) -- 深拷贝" << std::endl;
		string tmp(s);
		swap(tmp);
		return *this;
	}
	
	~string()
	{
		delete[] _str;
		_str = nullptr;
	}
	char& operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}
	void reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	void push_back(char ch)
	{
		if (_size >= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}
		_str[_size] = ch;
		++_size;
		_str[_size] = '\0';
	}
	//string operator+=(char ch)
	string& operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	const char* c_str() const
	{
		return _str;
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity; // 不包含最后做标识的\0
};

左值引用的短板
当函数返回对象是一个局部变量,除了函数作用域就不存在了,不能使用左值引用返回,只能传值返回。例如:string to_string(int value)函数中可以看到,这里只能传值返回,传值返回会导致至少一次拷贝构造(如果旧一点的编译器可能是两次拷贝构造)

string to_string(int value)
{
	string ret;
	while (value)
	{
		int x = value % 10;
		value /= 10;
		ret += '0' + x;
	}

	std::reverse(ret.begin(), ret.end());
	return ret;
}

旧编译器会产生两次拷贝构造,返回的ret对象拷贝一次,赋值的时候也会调用一次赋值重载。既然ret已经是一个要销毁的对象了,多次拷贝就会造成资源的浪费。下面第一个是连续的构造和拷贝构造都可以优化一次拷贝构造
在这里插入图片描述
下面这个无法优化
在这里插入图片描述
右值引用和移动语义解决上述问题
右值可以分为:
1.纯右值,内置类型右值
2.将亡值,自定义的右值
移动构造本质是将参数右值的资源窃取过来,占为己有,不做深拷贝,叫它移动构造,就是窃取别人的资源构造自己。所以可以实现拷贝和赋值的移动版本
编译器会选择最匹配的调用,to_string返回的是右值,如果既有拷贝又有右值,就会匹配移动构造

//移动拷贝
string(string&& s)
{
	std::cout << "string(string&& s) -- 移动语义" << std::endl;
	swap(s);
}
//移动赋值
string& operator=(string&& s)
{
	std::cout << "string& operator=(string s) -- 移动语义" << std::endl;
	swap(s);
	return *this;
}

在这里插入图片描述
s和ret的字符串是同一个

运行后调用了一次移动构造和移动赋值,因为如果用一个已经存在的对象接收,编译器没办法优化,to_string函数中先用str生成构造一个临时对象,但是可以看到,编译器把str识别成了右值,调用了移动构造,然后把临时对象作为to_string函数调用的 返回值赋值给ret1,调用的移动赋值

stl容器都增加了移动构造和移动赋值

string s1("hello");
string s2 = s1;
string s3 = std::move(s1);

6.4 右值引用左值及一些深入的使用场景

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?有些场景下,需要用右值去引用左值实现移动语义。当需要右值引用左值时,可以通过move函数将左值转化为右值,c++11中,std::move()函数位于头文件中,该函数名字具有迷惑性,并不搬移任何东西,唯一的功能是将一个左值强制转换为右值使用,实现移动语义

template<class _Ty>
 inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
 {
 // forward _Arg as movable
 return ((typename remove_reference<_Ty>::type&&)_Arg);
 }
//move会改为右值,s1的资源置空,转移给了s3
string s1("hello");
string s2 = s1;
string s3 = std::move(s1);
//move的返回值是右值,并不改变变量本身
string s4 = s1;

stl容器也加入了右值引用版本
在这里插入图片描述

std::list<string> l1;
string s1 = "hello";
//左值
l1.push_back(s1);
//右值
l1.push_back(to_string(1234));

运行结果:
// string(const string& s) -- 深拷贝
// string(string&& s) -- 移动语义

在这里插入图片描述

修改list
在前面的list中加入右值插入的版本,先把pushback函数加入右值,这时还是会有深拷贝
右值被右值引用以后得属性是左值,编译器设计因为右值引用需要被修改

在这里插入图片描述
这里x传入下层又变为了左值,需要传给inert的右值版本move后的,insert函数里创建节点也需要再次move

__list_node(T&& x)
	: _prev(nullptr)
	, _next(nullptr)
	, _data(std::move(x))
{}

void push_back(T&& x)
{
	Insert(end(), std::move(x));
}

void Insert(iterator pos, T&& x)
{
	node* new_node = new node(std::move(x));
	//记录前后节点
	node* pre = pos.node_iterator->_prev;
	node* cur = pos.node_iterator;
	//连接
	pre->_next = new_node;
	new_node->_prev = pre;

	new_node->_next = cur;
	cur->_prev = new_node;

}

list<string> l1;
string s1 = "hello";
l1.push_back(s1);
l1.push_back(to_string(1234));

在这里插入图片描述

6.5 完美转发

模板中的&&万能引用

void fun(int& x) { std::cout << "左值引用" << std::endl; };
void fun(const int& x) { std::cout << "const 左值引用" << std::endl; };
void fun(int&& x) { std::cout << "右值引用" << std::endl; };
void fun(const int&& x) { std::cout << "const 右值引用" << std::endl; };

// 模板的&&不是右值引用,是万能引用,既能接收左值,也能右值
// 引用类型的唯一作用是限制了接收的类型,后续使用都退化成了左值
// 想要保持左值和右值的属性,要使用完美转发
template <typename T>
void PerfectForward(T&& t)
{
	fun(t);
};

PerfectForward(10);  //右值
int a;
PerfectForward(a);  //左值
PerfectForward(std::move(a));  //右值
const int b = 8;
PerfectForward(b);  //左值
PerfectForward(std::move(b));  //右值

上面的t后续都退化成了左值,想要保持传入的属性,就要加入std::forward保留属性

fun(std::forward<T>(t));

使用场景
容器的插入等可以直接使用完美转发,代替左值和右值两个版本

7. 新的类功能

默认成员函数
原来c++类中,有6个默认成员函数:
1.构造函数
2.析构函数
3.拷贝构造函数
4.拷贝赋值重载
5.取地址重载
6.const取地址重载

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的
c++11新增了两个:移动构造函数和移动赋值运算符重载

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

  • 如果没有实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员汇之星逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
  • 如果没有实现移动赋值重载,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员汇之星逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动,没有实现就调用拷贝构造
  • 如果提供了移动构造或者移动赋值,编译器就不会自动提供拷贝构造和拷贝赋值

类成员变量初始化
c++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化

强制生成默认函数的关键字default
c++11可以更好的控制要使用的默认函数,假设要使用某个默认的函数,但因为一些原因没有默认生成,比如提供了拷贝构造,就不会生成移动构造,可以适用default关键字显示指定移动构造生成

class Person
 {
 public:
 Person(const char* name = "", int age = 0)
 :_name(name)
 , _age(age)
 {}
 Person(const Person& p)
 :_name(p._name)
 ,_age(p._age)
 {}
 Person(Person&& p) = default;
 private:
 bit::string _name;
 int _age;
 };
 int main()
 {
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 return 0;
 }

禁止生成默认函数的关键字delete
如果想要限制某些默认函数的生成,在c++98中,是该函数设置成private,并且只声明补丁,止痒只要其他人想要调用就会报错。在c++11中更简单,只需在函数声明上加上=delete即可,指示编译器不生成对应函数的默认版本,修饰的为删除函数

class Person
 {
 public:
 Person(const char* name = "", int age = 0)
 :_name(name)
 , _age(age)
 {}
 Person(const Person& p) = delete;
 private:
 bit::string _name;
 int _age;
 };
 int main()
 {
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 }
 

继承和多态中的final与override关键字
final修饰类或虚函数,表示不可被继承或重写。override检测虚函数是否完成重写

8. 可变参数模板

c++11的新特性可变参数模板能够创建可以接收可变参数的函数模板和类模板,相比c++98/03,类模板和函数模板中只能含固定数量的模板参数,可变模板参数无疑是一个巨大的改进。然而由于可变模板参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。掌握一些基础的可变模板参数特性就可以了

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
 void ShowList(Args... args)
 {}

上面的参数args前面有省略号,所以它就是一个可变模板参数,把带省略号的参数称为“参数包”,里面包含了0到N(N>0)个模板参数。无法直接获取参数包args中的每个参数,只能通过展开参数包的方式获取每个参数,这时使用可变模板参数的一个主要特点,也是最大的难点,如何展开可变模板参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们用一些特殊方式一一获取参数包

递归函数展开参数包

// 递归终止函数
void _ShowList()
{
	std::cout << std::endl;
}
//展开函数
template <class T, class ...Args>
void _ShowList(const T& value, Args ...args)
{
	std::cout << value << " ";
	_ShowList(args...);
}

template <class ...Args>
void ShowList(Args ...args)
{
	_ShowList(args...);
}

int main()
{
	ShowList(1, 2, 'x');
	ShowList(1, 2, 3.5);

	return 0;
}

逗号表达式展开
这种方式展开参数包,不需要通过递归终止函数,是直接在expand函数体中展开的,printarg不是一个递归终止函数,指示一个处理参数包每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。逗号表达式会按顺序执行,返回最后一个

expand函数中的逗号表达式也(printarg(args),0),也是按照这个执行顺序,限制性printarg(args),在得到逗号表达式的结果0,同时还用到了c++11的另外一个特性–初始化列表,通过初始化列表来初始化一个变长数组,{(printarg(args), 0}将会展开成(printarg(arg1), 0),(printarg(arg2), 0), (printarg(arg3), 0), etc…),最终会创建一个元素值都为0的数组Int arr[sizeof…(args)]。由于是逗号表达式,在创建数组的过程中回显执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个参数的目的纯粹是为了在数组构造的过程展开参数包

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);
 ShowList(1, 'A');
 ShowList(1, 'A', std::string("sort"));

stl容器中empalce相关接口

 template <class... Args>
 void emplace_back (Args&&... args);

emplace系列接口,支持模板的可变参数,并且万能引用。那么相对insert优势在哪里

int main()
 {
 std::list< std::pair<int, char> > mylist;
 // emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
// 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
mylist.emplace_back(10, 'a');
 mylist.emplace_back(20, 'b');
 mylist.emplace_back(make_pair(30, 'c'));
 mylist.push_back(make_pair(40, 'd'));
 mylist.push_back({ 50, 'e' });
 for (auto e : mylist)
 cout << e.first << ":" << e.second << endl;
 return 0;
 }

emplace是由模板参数包直接传入参数,不会拷贝一个临时对象。而pushback需要拷贝构造或移动构造。移动构造的消耗也不是很高

int main()
 {
 // 下面我们试一下带有拷贝构造和移动构造的bit::string,再试试呢
// 我们会发现其实差别也不到,emplace_back是直接构造了,push_back
 // 是先构造,再移动构造,其实也还好。
std::list< std::pair<int, bit::string> > mylist;
 mylist.emplace_back(10, "sort");
 mylist.emplace_back(make_pair(20, "sort"));
 mylist.push_back(make_pair(30, "sort"));
 mylist.push_back({ 40, "sort"});
 return 0;
 }

在这里插入图片描述

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

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

相关文章

【桌面微信多开】

桌面微信多开 步骤一&#xff1a;新建txt步骤二&#xff1a;保存修改为.bat步骤三&#xff1a;双击运行程序步骤四&#xff1a;多次点击微信 步骤一&#xff1a;新建txt echo offstart /d "D:\program\WeChat\" WeChat.exestart /d "D:\program\WeChat\" …

Xilinx FPGA:vivado利用单端RAM/串口传输数据实现自定义私有协议

一、项目要求 实现自定义私有协议&#xff0c;如&#xff1a;pc端产生数据&#xff1a;02 56 38 &#xff0c;“02”代表要发送数据的个数&#xff0c;“56”“38”需要写进RAM中。当按键信号到来时&#xff0c;将“56”“38”读出返回给PC端。 二、信号流向图 三、状态…

MessageBox的作用与用法

在C# &#xff08; Windows Forms &#xff09;中&#xff0c;MessageBox 的所有常用用法如下&#xff1a; 1. 显示一个简单的消息框 MessageBox.Show("这是一个简单的消息框。");2. 显示带标题的消息框 MessageBox.Show("这是一个带标题的消息框。", &…

美国服务器租用详细介绍与租用流程

在数字化时代&#xff0c;服务器租用已成为许多企业和个人拓展业务、存储数据的重要选择。美国作为全球科技发展的前沿阵地&#xff0c;其服务器租用服务也备受瞩目。下面&#xff0c;我们将详细介绍美国服务器租用的相关知识及租用流程。 一、美国服务器租用简介 美国服务器租…

PDI-kettle工具连接本地虚拟机Ubuntu上的数据库

PDI 配置ubuntu数据库配置Kettle工具 PDI版本&#xff1a;9.4 Ubuntu2204&#xff1a;10.11.8-MariaDB-0ubuntu0.24.04.1 Ubuntu 24.04 配置ubuntu数据库 安装 apt install -y mariadb-server配置监听地址 cat > /etc/mysql/mariadb.conf.d/99-kettle.cnf << EOF …

结构体------“成绩排序”---冒泡----与“输出最高成绩”区别

从大到小或者从小到大排序----冒泡排序---双重循环i,j 比较的时候用的是 排序的时候用的是整体 stu [ j1 ] 和 stu [ j ] 我写错为下面这个&#xff0c;交换的只是学生的出生日期&#xff0c;没有交换整体 #include<stdio.h> #include<string.h>struct student{ch…

MySQL 8.0新特性INTERSECT和EXCEPT用于集合运算

MySQL8.0.31 新版本的推出&#xff0c;MySQL增加了对SQL标准INTERSECT和EXCEPT运算符的支持。 1、INTERSECT INTERSECT输出多个SELECT语句查询结果中的共有行。INTERSECT运算符是ANSI/ISO SQL标准的一部分(ISO/IEC 9075-2:2016(E))。 我们运行两个查询&#xff0c;第一个会列…

仰邦BX.K协议对接

背景 使用BX 6K控制卡控制诱导屏显示剩余车位数&#xff0c;由于控制卡和服务端不在一个局域网内&#xff0c;所以不能使用官网提供的案例&#xff0c;官网提供的案例为控制卡为TCP Server&#xff0c;服务端为TCP Client&#xff0c;因此需要开发此程序&#xff0c;服务端左右…

docker mysql cpu100% cpu打满排查 mysql cpu爆了 mysql cpu 100%问题排查

1. docker 启动了一个mysql 实例&#xff0c;近期忽然发现cpu100% 如下图所示 命令&#xff1a; top 2.进入容器内排查&#xff1a; docker exec mysql&#xff08;此处可以是docker ps -a 查找出来的image_id&#xff09; -it /bin/bash cd /var/log cat mysqld.log 容器内m…

Android LayoutInflater 深度解析

在 Android 开发中&#xff0c;LayoutInflater 是一个非常重要的工具。它允许我们从 XML 布局文件中动态地创建 View 对象&#xff0c;从而使得 UI 的创建和管理更加灵活。本文将深入解析 android.view.LayoutInflater&#xff0c;包括它的基本用法、常见问题以及高级用法。 什…

Java高级重点知识点-19-Lambda

文章目录 Lambda表达式函数式编程思想Lambda表达式写法代码讲解 Lambda表达式 函数式编程思想 强调做什么&#xff0c;而不是以什么形式做。 以函数式接口Runnable为例讲解&#xff1a; public class LambdaDemo {public static void main(String[] args) {Runnable runnab…

学习无人机飞行技术,有哪些就业方向?

随着无人机技术的不断进步和应用领域的拓展&#xff0c;研发创新人才的需求也将不断增加&#xff0c;那就业前景还是很广阔的。学习无人机飞行技术后&#xff0c;有以下多个就业方向可供选择&#xff1a; 1. 无人机操作员&#xff1a; - 负责操控和监控无人机飞行&#xff0c;…

第57期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

“探索价值增长消费:让每一笔购物都成为增值之旅“

亲爱的顾客们&#xff0c;你们好&#xff01;今天&#xff0c;我将带你们探索一种革命性的消费哲学——价值增长消费&#xff0c;让每一次购物都成为一次增值之旅&#xff01; 在传统消费观念里&#xff0c;我们付出金钱换取商品或服务&#xff0c;随后这些便成为过去。但如今…

38. 扫描系统设计

导论: 扫描系统根据反射旋转类型分为平面振镜扫描和转股扫描&#xff0c;根据光路途径分为一维、二维和三维扫描&#xff0c;根据振镜和扫描镜头的位置又可分为镜前扫描和镜后扫描。 设计流程&#xff1a; zemax设计与优化&#xff1a; 系统建模 在孔径类型中选择入瞳直径…

fastapi swagger js css 国内访问慢问题解决

fastapi swagger js css 国内访问慢问题解决 直接修改fastapi包中静态资源地址为如下地址 swagger_js_url: str "https://cdn.bootcdn.net/ajax/libs/swagger-ui/3.9.3/swagger-ui-bundle.js", swagger_css_url: str "https://cdn.bootcdn.net/ajax/libs/sw…

发电机保护屏的作用及其重要性

发电机保护屏的作用及其重要性 发电机保护屏是电力系统中的重要组成部分&#xff0c;它负责监测和控制发电机的运行状态&#xff0c;确保发电机在正常运行和故障情况下都能得到及时、准确的保护。负责监测和控制发电机运行状态&#xff0c;确保及时准确的保护。它由显示屏、控制…

python学习-list

List(列表的定义语法) [元素1, 元素2, 元素3, ......] 什么是元素&#xff1f; 数据容器内的每一份数据&#xff0c;都称之为元素 元素的类型有限制吗&#xff1f; 元素的数据类型没有任何限制&#xff0c;甚至元素也可以是列表&#xff0c;这样就定义了嵌套列表 但是打印…

RT-Thread Studio与CubeMX联合编程之rtthread stm32h743的使用(十一)spi设备SFUD驱动的使用

我们要在rtthread studio 开发环境中建立stm32h743xih6芯片的工程。我们使用一块stm32h743及fpga的核心板完成相关实验&#xff0c;核心板如图&#xff1a; 1.建立新工程&#xff0c;选择相应的芯片型号及debug引脚及调试器 2.编译下载&#xff0c;可以看到串口打印正常 3.…