C++_C++11的学习

news2025/1/2 3:08:20

1. 统一的列表初始化

1.1{}初始化

        在C++98 中,标准就已经允许使用花括号 {} 数组或者结构体元素进行统一的列表初始值设定。而到了C++11,标准扩大了用大括号括起的列表 ( 初始化列表 )的使用范围,使其能适用于所有的内置类型和自定义类型,而且使用初始化列表时可用可不用=
struct Point
{
	int _x;
	int _y;
};
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;
};
int main()
{
	int x1 = 1;
	int x2{ 2 };
	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };
	Point p{ 1, 2 };

	// C++11中列表初始化也可以适用于new表达式中
	int* pa = new int[4] { 0 };

	//C++98
	Date d1(2022, 1, 1);
	//C++11
	Date d2{ 2022, 1, 2 };
	Date d3 = { 2022, 1, 3 };
	return 0;
}

1.2 std::initializer_list

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

int main()
{
	vector<int> v = { 1,2,3,4 };
	list<int> lt = { 1,2 };
	// 这里{"sort", "排序"}会先初始化构造一个pair对象
	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	// 使用大括号对容器赋值
	v = { 10, 20, 30 };
	return 0;
}

以vector为例,讲一下initializer_list的使用:
        在C++11中,vector新增了如下的构造函数

	//先构造再拷贝 -> 编译器优化 -> 直接构造
	vector<int> v1 = { 1,2,3,4,5,6,7,8,9 };
	//直接构造
	vector<int> v2{ 1,2,3,4,5,6,7,8,9 };

2. 声明

2.1 auto

        在C++98 中auto是一个存储类型的说明符,表明变量是局部自动存储类型。 但是局部域中定义局 部的变量默认就是自动存储类型,所以 auto 就没什么价值了。 C++11中废弃 auto 原来的用法,将 其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。 因此 auto 并非是一种 类型 的声明,而是一个类型声明时的 占位符 ,编译器在编
译期会将 auto 替换为变量实际的类型。

2.1.1 auto的使用细则

1. auto与指针和引用结合起来使用
        用auto声明指针类型时,用
autoauto*没有任何区别,但用auto声明引用类型时则必须&

2. 在同一行定义多个变量
        当在同一行声明多个变量时,这些变量必须是相同的类型,否则,编译器将会报错,因为编译 器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

2.1.2 auto不能推导的场景

1. auto 不能作为函数的参数
        因为编译器无法对函数参数的实际类型进行推导
2. auto 不能直接用来声明数组

 

2.2 decltype

关键字decltype将变量的类型声明为表达式指定的类型

template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}
int main()
{
	const int x = 1;
	double y = 2.2;
	decltype(x * y) ret; // ret的类型是double
	decltype(&x) p;      // p的类型是int*
	cout << typeid(ret).name() << endl;
	cout << typeid(p).name() << endl;
	F(1, 'a');
	return 0;
}

2.3 nullptr

        由于C++ NULL 被定义成字面量 0 ,这样就可能回带来一些问题,因为 0 既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11 中新增了 nullptr ,用于表示空指针。
注意:
        1. 在使用 nullptr 表示指针空值时不需要包含头文件,因为 nullptr C++11 作为新关键字
        2. 在 C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。

3. 基于范围的for循环

3.1 范围for的语法

        对于一个有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11 中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范 围内用于迭代的变量,第二部分则表示被迭代的范围
int main() {
	int array[] = { 1, 2, 3, 4, 5 };
	// 如果要进行修改,就必须要引用
	for (auto& e : array)
		e *= 2;
	// 如果只是访问,可以不用引用
	for (auto e : array)
		cout << e << " " << endl;
	return 0;
}
注意:与普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环。

3.2 范围for的使用条件

1. for 循环迭代的范围必须是确定的
        对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和 end 的方法, begin end 就是 for 循环迭代的范围。
2. 迭代的对象要实现 ++ == 的操作
        因为对于编译器,编译器范围for的使用最后还是要转化为一般for循环的遍历
int main() {
	vector<int> v = { 1, 2, 3, 4, 5 };
	vector<int>::iterator it = v.begin();

	for (auto e : v)
		cout << e << " ";

	cout << endl;

	for (; it != v.end(); it++)
		cout << *it << " ";

	return 0;
}

4. STL中一些变化

4.1 新容器

        在原有容器的基础上新加了如下所标记的容器

 4.2 新方法

1.提供了cbegincend方法返回const迭代器等等
        但是实际意义不大,因为begin和end
也是可以返回const迭代器的。
2.
提供了各容器的initializer_list构造
3.push系列、insert、emplace等函数增加了右值引用的插入版本
4.容器增加了移动构造和移动赋值

总结:C++11引入了右值引用和移动语义,解决了左值引用的一些问题并极大提高了效率

5. 新的类功能

5.1 默认成员函数

原来C++ 类中,有 6 个默认成员函数:
        1. 构造函数
        2. 析构函数
        3. 拷贝构造函数
        4. 拷贝赋值重载
        5. 取地址重载
        6. const 取地址重载
        最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。C++11 新增了两个:移动构造函数移动赋值运算符重载
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
        1.如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
        2.如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝;自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。
        3.如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

5.2 类成员变量初始化

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

5.3 强制生成默认函数的关键字default

        C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default 关键字显示指定移动构造生成。

5.4 禁止生成默认函数的关键字delete

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

5.5 继承和多态中的finaloverride关键字

1.final: 修饰时,它表示该类不能被继承
             修饰虚函数时,它表示该虚函数不能在子类中被重写
2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

6. 可变参数模板

        C++11的新特性可变参数模板能够让您创建可以接受可变参数函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数是一个巨大的改进。
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
        我们把带省略号的参数称为“ 参数包” ,它里面包含了 0 N N>=0 )个模版参数。我们无法直接获取参数包 args 中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,下面是主要的两种方式:

1. 递归函数方式展开参数包

// 每次递归都会获取并显示1个参数直至全部参数获取完就结束
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

2. 逗号表达式展开参数包

        PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式:
        函数中的逗号表达式:(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函数打印参数,这个数组的目的纯粹是为了在数组构造的过程展开参数包

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"));
	return 0;
}

3. STL容器中的empalce相关接口函数

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

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

        STL中的容器大多都实现了可参数列表式的empalce式的接口函数,它和insert、push式的函数比较如下:
1. 插入类型是单个值,两个没什么区别
2. 直接给插入对象参数时,empalce系列对于深拷贝的类对象,减少一次移动构造,对于浅拷贝的类对象,减少一次拷贝构造

7. lambda表达式

7.1 书写格式

lambda 表达式书写格式: [capture-list] (parameters) mutable -> return-type { statement }
lambda 表达式各部分说明:
1.[capture-list] : 捕捉列表 ,该列表总是出现在 lambda 函数的开始位置, 编译器根据 [] 判断接下来的代码是否为 lambda 函数 捕捉列表能够捕捉上下文中的变量供 lambda 函数使用 (必写)
2.(parameters) :参数列表。与 普通函数的参数列表一致 ,如果不需要参数传递,则可以连同() 一起省略。
3.mutable :默认情况下, lambda 函数总是一个 const 函数, mutable 可以取消其常量性。使用该修饰符时,参数列表不可省略( 即使参数为空 )
4.returntype:返回值类型。用 追踪返回类型形式声明函数的返回值类型 ,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
5.{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量. (必写)
注意:
        在lambda 函数定义中, 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以 。因此 C++11 最简单的 lambda 函数为: []{} ; lambda函数不能做任何事情。 表达式实际上可以理解为无名函数,在语法层没有类型,该函数无法直接调 用,如果想要直接调用,可借助 auto 将其赋值给一个变量,或者用decltype推导表达式的类型,lambda对象禁止了默认构造。
void Test()
{
	auto f = [] {cout << "hello world" << endl; };
	f();
}

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}

	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "Date(const Date& d)" << endl;
	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	auto DateLess = [](const Date* p1, const Date* p2)
	{
		return p1 < p2;//偷懒直接比指针
	};
	cout << typeid(DateLess).name() << endl;

	// lambda对象支持拷贝构造
	auto copy(DateLess);

	// lambda对象禁掉默认构造
	// decltype(DateLess) xx;//运行报错

    //为了下面的代码运行通过,还得有比较对象DateLess,因为lambda对象不会默认构造
	priority_queue<Date*, vector<Date*>, decltype(DateLess)> p1(DateLess);

	return 0;
}

7.2 函数对象与lambda表达式

        函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator() 运算符的类对象。
class Rate
{
public:
	Rate(double rate) : _rate(rate)
	{}

	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};

int main()
{
	double rate = 0.49;
	Rate r1(rate);
	r1(10000, 2);

	auto r2 = [=](double monty, int year)->double {return monty * rate * year;};
	r2(10000, 2);

	auto f1 = [] {cout << "hello world" << endl; };
	auto f2 = [] {cout << "hello world" << endl; };

	f1();
	f2();

	return 0;
}

        通过查看汇编代码可以知道,编译器对于lambda表达式的处理最终还是转换成对仿函数的处理,而且还可以知道,编译器对这个转换后的仿函数的地址对于相同的lambda表达式是不同的,这就解释了为什么lambda表达式间是不支持赋值的,即使看上去类型相同。

8. 包装器

8.1 function包装器

//std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret : 被调用函数的返回类型
Args…:被调用函数的形参

        function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器,是对可调用对象的再封装,统一类型,可调用对象有函数指针/函数名、仿函数、lambda表达式等。   
        
 对于编译器而言,下面的代码模useF板会实例化3份:

template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;

	return f(x);
}

double f(double i)
{
	return i / 2;
}

struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};

int main()
{
	// 函数名
	cout << useF(f, 11.11) << endl;

	// 函数对象
	cout << useF(Functor(), 11.11) << endl;

	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;

	return 0;
}

 

         而如果使用function进行封装后得到的可调用类型是一样的,实例化就只有1份,function的使用无疑减小了模板实例化的开销:

int main()
{
	// 函数指针
	function<double(double)> fc1 = f;
	fc1(11.11);
	cout << useF(fc1, 11.11) << endl;

	// 函数对象
	function<double(double)> fc2 = Functor();
	fc2(11.11);
	cout << useF(fc2, 11.11) << endl;

	// lambda表达式
	function<double(double)> fc3 = [](double d)->double { return d / 4; };
	fc3(11.11);
	cout << useF(fc3, 11.11) << endl;

	return 0;
}

        除了上述的可调用对象,function还能对类的(静态)成员函数进行封装:

int f(int a, int b)
{
	return a + b;
}

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a + b;
	}
};

int main()
{
	// 普通函数
	function<int(int, int)> fc1 = f;
	cout << fc1(1, 1) << endl;

	// 静态成员函数
	function<int(int, int)> fc2 = &Plus::plusi;
	cout << fc2(1, 1) << endl;

	// 非静态成员函数
	// 非静态成员函数需要对象的指针或者对象去进行调用
    // 因为非静态成员函数还有一个隐式参数:this

	/*Plus plus;
	function<double(Plus*, double, double)> fc3 = &Plus::plusd;
	cout << fc3(&plus, 1, 1) << endl;*/

	function<double(Plus, double, double)> fc3 = &Plus::plusd;
	cout << fc3(Plus(), 1, 1) << endl;

	return 0;
}

8.2 bind

template <class Fn, class... Args>
  /* unspecified */ bind (Fn&& fn, Args&&... args);

template <class Ret, class Fn, class... Args>
  /* unspecified */ bind (Fn&& fn, Args&&... args);
        std::bind函数定义在头文件中, 是一个函数模板,它就像一个函数包装器 ( 适配器 ) 接受一个可 调用对象 ,生成一个新的可调用对象来 适应 原对象的参数列表
        调用bind 的一般形式: auto newCallable = bind(callable,arg_list); 其中,newCallable 本身是一个可调用对象, arg_list 是一个逗号分隔的参数列表,对应给定的callable的参数。 当我们调用 newCallable 时, newCallable 会调用 callable, 并传给它 arg_list 的参数
        arg_list中的参数可能包含形如 _n 的名字,其中 n 是一个整数,这些参数是 占位符 ,表示
newCallable 的参数,它们占据了传递给 newCallable 的参数的 位置 。数值 n 表示生成的可调用对象中参数的位置:_1 newCallable 的第一个参数, _2 为第二个参数,以此类推。 它的功能如下:

1. 调整参数顺序(意义不大)

int Sub(int a, int b)
{
	return a - b;
}

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a - b;
	}
};

int main()
{
	// 调整参数顺序,意义不大
	int x = 10, y = 20;
	cout << Sub(x, y) << endl;

	auto f1 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << f1(x, y) << endl;

	function<double(Plus, double, double)> fc3 = &Plus::plusd;
	cout << fc3(Plus(), 1, 1) << endl;

	return 0;
}

2. 调整参数个数(意义大)

int main()
{
	// 调整参数的个数
	// 某些参数绑死
	function<double(double, double)> fc4 = bind(&Plus::plusd, Plus(), placeholders::_1, placeholders::_2);
	cout << fc4(2, 3) << endl;

	function<double(double)> fc5 = bind(&Plus::plusd, Plus(), placeholders::_1, 20);
	cout << fc5(2) << endl;

	return 0;
}

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

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

相关文章

思科模拟器--03.RIP协议路由--24.5.17

1.首先&#xff0c;先创建两个个人电脑:PC0和PC1和三个路由器:R1&#xff0c;R2和R3. (诀窍:建议用文本框标注一下重要简短的内容; 目的:降低失误概率,提高成功率!) 第0步:(个人电脑的IP,子网掩码和默认网关配置) 接着&#xff0c;可以先将个人电脑的IP和网关先配置一下…

虹科Pico汽车示波器 | 免拆诊断案例 | 2017款奔驰E300L车行驶中发动机偶尔无法加速

故障现象 一辆2017款奔驰E300L车&#xff0c;搭载274 920发动机&#xff0c;累计行驶里程约为21万km。车主反映&#xff0c;该车行驶中发动机偶尔无法加速&#xff0c;且车辆发闯。 故障诊断 用故障检测仪检测&#xff0c;发动机控制单元&#xff08;N3/10&#xff09;中存储…

由于下列错误 luafv服务启动失败的解决办法

主要是电脑近期总有问题&#xff0c;经常使用中就死机&#xff0c;无任何反应只能按重启按钮。 一天最少也要有一次&#xff0c;然后查看死机前的系统日志发现主要错误为 “由于下列错误&#xff0c;luafv 服务启动失败:此驱动程序被阻止加载” 该错误在每天都会出现&#x…

ChatGPT移动应用收入在GPT-4o发布后迎来最大涨幅

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

几年前写的一个小工具

几年前写的一个工具&#xff0c;开发工具 是Delphi7 UniDAC FastReport2.53 &#xff0c;开发时间不到8小时&#xff08;同时还在处理其他事情&#xff09;。 其实把这个翻出来&#xff0c;是想说说俺的一个同事。他是俺这几年遇到的最优秀的人之一。他负责售后维护部&#x…

Harmony学习笔记一——项目创建及配置

文章基于Harmony Next Preview2 进行学习&#xff0c;其他版本可能会稍有不同 准备工作 由于目前Harmony Next仅有Preview版本&#xff0c;想要进行Harmony Next开发需要向华为申请权限&#xff0c;具体操作参考: https://developer.huawei.com/consumer/cn/forum/topic/02081…

一文搞懂HashSet类的底层实现原理

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

Flask多线程开发指南

文章目录 1. 什么是多线程&#xff1f;2. Flask中的多线程3. 注意事项结论 在Web应用程序开发中&#xff0c;有时候需要处理一些耗时的任务&#xff0c;例如与数据库交互、发送网络请求或执行计算密集型的操作。为了保持用户体验的流畅性&#xff0c;我们可以使用多线程来处理这…

【开源可视化报表设计器】借力实现高效率流程化办公!

进行数字化转型、实现流程化办公&#xff0c;这些应该是目前很多企业都想要实现的目标吧。那么&#xff0c;利用什么样的软件平台可以实现&#xff1f;低代码技术平台拥有可视化界面、灵活操作、好维护等众多优势特点&#xff0c;可以借助低代码技术平台、开源可视化报表设计器…

Hidedump:dumplsass加密免杀工具

文章目录 前记hook WriteAllduplication其他思路SilentProcessExitminidumpCallback 后记referencereference 前记 思路&#xff1a;直接dumplsass原文会被杀软删掉&#xff0c;通过hook WriteAll对dump的内容先加密再保存到磁盘并离线解密 项目已开源&#xff0c;该项目采用…

【git】开发提交规范(feat、fix、perf)

这段时间收到的需求很多&#xff0c;可能是临近两周一次的大版本灰度上线&#xff0c;这次产生了一个关于git的思考&#xff0c;就是各个版本之间怎么管理的问题&#xff0c;这里做出我自己的一些方法。 首先&#xff0c;既然已经明确了remote分支中的release分支为主分支&…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…

爬虫学习--11.MySQL数据库的基本操作(上)

MySQL数据库的基本操作 创建数据库 我们可以在登陆 MySQL 服务后&#xff0c;使用命令创建数据库&#xff0c;语法如下: CREATE DATABASE 数据库名; 显示所有的数据库 show databases; 删除数据库 使用普通用户登陆 MySQL 服务器&#xff0c;你可能需要特定的权限来创建或者删…

java中的StringBuffer类和StringBuildet类

一、StringBuffer类 1、特点 底层是不被final修饰的char数组value,数组地址可以发生改变&#xff0c;当StringBuffer类对象的值发生改变时不用创建新的对象 2、构造方法 3、扩容规则 StringBuffer的底层数组value在扩容时为当前数组的长度2倍加2或者当前数组长度加上要追加…

CVE-2020-7982 OpenWrt 远程命令执行漏洞学习(更新中)

OpenWrt是一款应用于嵌入式设备如路由器等的Linux操作系统。类似于kali等linux系统中的apt-get等&#xff0c;该系统中下载应用使用的是opgk工具&#xff0c;其通过非加密的HTTP连接来下载应用。但是其下载的应用使用了SHA256sum哈希值来进行检验&#xff0c;所以将下载到的数据…

如何将短信从 iPhone 传输到计算机 - 5 种灵活的方式

概括 与他人交流的多种方式之一是发送短信。对于那些包含珍贵回忆或重要信息的消息&#xff0c;您需要将它们发送到另一台设备&#xff08;例如计算机&#xff09;进行备份。将短信文件发送到计算机是保存宝贵数据的最佳方法之一。继续阅读本文&#xff0c;了解如何将短信从 i…

深度学习 | 复杂注意力神经网络 —— 大模型

前面讲解了注意力神经网络 一、BERT模型 1、什么是BERT 它是由谷歌在2018年提出的 双向Transformer 编码器模型。 Bidirectional Encoder Representations from Transformers. 主要使用了Transformer的编码器 Transformer 编码器堆叠&#xff1b; 预训练 精调两步结构。 BERT…

FreeBSD/Linux下的系统资源监视器排队队

bpytop bpytop 是一个基于 Python 的资源监视器&#xff0c;可以在 FreeBSD 上使用。它提供了对文件写入磁盘、网络、CPU 和内存占用的监视功能。 pkg install bpytop 或者用ports安装 cd /usr/ports/sysutils/bpytop/ make install clean bashtop bashtop 也是一个基于 P…

微服务如何做好监控

大家好&#xff0c;我是苍何。 在脉脉上看到这条帖子&#xff0c;说阿里 P8 因为上面 P9 斗争失败走人&#xff0c;以超龄 35 被裁&#xff0c;Boss 上找工作半年&#xff0c;到现在还处于失业中。 看了下沟通记录&#xff0c; 沟通了 1000 多次&#xff0c;但没有一个邀请投递…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…