【C++11那些事儿(一)】

news2025/1/17 6:07:28

文章目录

  • 一、C++11简介
  • 二、列表初始化
    • 2.1 C++98中{}的初始化问题
    • 2.2 C++11中的列表初始化
  • 三、各种小语法
    • 3.1 auto
    • 3.2 decltype
    • 3.3 nullptr
    • 3.4 范围for
  • 四、STL中的一些变化
  • 五、左/右值引用和移动语义(本篇重点)
    • 5.1 做值引用和右值引用
    • 5.2 左值引用与右值引用比较
    • 5.3 右值引用使用场景和意义
    • 5.4 万能引用
    • 5.5 完美转发


一、C++11简介

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于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/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以很值得我们作为一个重点学习。C++11增加的语法特性篇幅非常多,没办法一一讲解,所以在我的文章中只讲比较实用的语法。

大家可以看一下C++11的官方网站,在读这篇文章之前了解一下
C++11官方网站

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


二、列表初始化

2.1 C++98中{}的初始化问题

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

int array1[] = {1,2,3,4,5};
int array2[5] = {0};

对于一些自定义的类型,却无法使用这样的初始化。比如:

vector<int> v{1,2,3,4,5};

就无法通过编译,导致每次定义vector时,都需要先把vector定义出来,然后使用循环对其赋初始值,非常不方便。C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

2.2 C++11中的列表初始化

C++11不同于C++98的变量初始化方法就体现在列表初始化上面,也就是说在C++11中,任何变量都可以直接用列表,在其创建是直接初始化,如下:

对于内置类型:
在这里插入图片描述
这么看来,好像也没厉害到那儿去,有一种脱了裤子放屁的感觉。
其实,C++11设着这个东西主要是为了在自定义类型的初始化中起作用。如下:
在这里插入图片描述

在C++98的标准中,对于自定义类型的变量,用动态管理的方式为它申请空间之后才能赋值。但在C++11标准中,可以直接用列表初始化。
注意,Point* p2 = new Point[2]{ {1,1},{2,2} };这种方式在VS2013中行不通,最后创建出来的变量还是没有初始化,可能是编译器的一个bug。要使用更高版本的编译器。

另外,C++11还可支持多个对象的列表初始化
多个对象想要支持列表初始化,需给该类(模板类)添加一个带有initializer_list类型参数的构造函数即可。注意:initializer_list是系统自定义的类模板,该类模板中主要有三个方法:begin()、end()迭代器以及获取区间中元素个数的方法size()。如下:
在这里插入图片描述

举个例子:

int main()
{
	auto li = { 1,2,3,4,5 };
	cout << typeid(li).name() << endl;
	return 0;
}

在这里插入图片描述

其实,可以理解为,花括号中的东西是一个常量数组,是存在于常量区的。然后编译器会将其中的值一一赋值给li。赋值的方式也是调用迭代器。

在这里插入图片描述
于是乎,就可以定义以下变量:
在这里插入图片描述


三、各种小语法

3.1 auto

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

如下:

int main()
{
	int i = 10;
	auto p = &i;
	auto pf = strcpy;
	cout << typeid(p).name() << endl;
	cout << typeid(pf).name() << endl;
	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();
	return 0;
}

在这里插入图片描述

3.2 decltype

上文中出现的typeid只能输出变量的类型,却不能用它再定义一个变量,而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;
}

在这里插入图片描述
但是这个东西真的不太常用(至少我还没用过)

3.3 nullptr

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

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

3.4 范围for

范围for的底层是一个迭代器,也是用来遍历一个容器(或者容器适配器),其用法比较简单,如下:
在这里插入图片描述

四、STL中的一些变化

C++11中增加了一些新容器,如下:
在这里插入图片描述
对于第一个array,本质上就是一个数组,与vector的区别是,vector是动态的,可以随时扩容。
array与vector和普通的数组最大的区别在于,它的越界访问机制非常严格,越界读和越界写都会被检查出来。而vector和普通数组对于越界读不做检查,对于越界写则采用抽查的方式。
(除此之外,array就没有什么太大的用处了)
读者也可以自己去cplusplus网站看一下它的用法,这里不再赘述。

下一个是forward_list,这是一个单向的链表。而list则是一个带头的双向循环链表。
相较于list,这个容器增加了头插和头删操作:
在这里插入图片描述

但是注意,它并不支持尾插尾删操作,因为这需要遍历找到尾结点,会导致效率大大降低。

对于另外的两个,感兴趣的读者可以自己去网站上看一看,这里不多解释了。另外,对于以上两个容器的介绍也不完整,大家也可以看一下。C++网站

五、左/右值引用和移动语义(本篇重点)

5.1 做值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名
什么是左值,什么是左值引用?

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

如下:

int main()
{
	// 以下的p、b、c、*p都是左值
	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;
	return 0;
}

什么是右值,什么是右值引用?

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

如下:

int main()
{
	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;
	return 0;
}

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用。

int main()
{
	 double x = 1.1, y = 2.2;
	 int&& rr1 = 10;
	 const double&& rr2 = x + y;
	 rr1 = 20;
	 rr2 = 5.5;  // 报错
	 return 0;
}

5.2 左值引用与右值引用比较

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。
int main()
{
    // 左值引用只能引用左值,不能引用右值。
    int a = 10;
    int& ra1 = a;   // ra为a的别名
    //int& ra2 = 10;   // 编译失败,因为10是右值
    // const左值引用既可引用左值,也可引用右值。
    const int& ra3 = 10;
    const int& ra4 = a;
    return 0;
}

右值引用总结:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
int main()
{
	 // 右值引用只能右值,不能引用左值。
	 int&& r1 = 10;
 
	 // error C2440: “初始化”: 无法从“int”转换为“int &&”
	 // message : 无法将左值绑定到右值引用
	 int a = 10;
	 int&& r2 = a;
	 // 右值引用可以引用move以后的左值
	 int&& r3 = std::move(a);
	 return 0;
}

对于上面代码中的move,下文中做出解释。

5.3 右值引用使用场景和意义

先来说一下引用的意义:
在函数传参和函数传返回值时,使用引用,可以达到减少拷贝的效果。
现在应该明白上面所说的引用,其实指的是左值引用。

左值引用有没有彻底解决问题?

答案是没有,要不然就没有右值引用什么事了。当某一个变量/对象出了作用域就不存在了,这种情况就不能用引用返回了,这是众所周知的。
而当需要返回的变量/对象是一个特别复杂的结构,就必须要传值返回,就要进行深拷贝,效率将大大降低。
而右值引用就将解决这个问题

为了解释它解决问题的原理,需要先自己实现一个string类,以便观察它解决的具体过程。string类代码如下:

namespace sny
{
	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)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
				cout << "string& operator=(string s) -- 深拷贝" << 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 = nullptr;
		size_t _size = 0;
		size_t _capacity = 0; // 不包含最后做标识的\0
	};
	string to_string(int value)
	{
		bool flag = true;
		if (value < 0)
		{
			flag = false;
			value = 0 - value;
		}
		sny::string str;
		while (value > 0)
		{
			int x = value % 10;
			value /= 10;
			str += ('0' + x);
		}
		if (flag == false)
		{
			str += '-';
		}
		std::reverse(str.begin(), str.end());
		return str;
	}
}

现在假设读者对上面类中的所有函数都已经理解了,然后再看下面这行代码:

int main()
{
	sny::string ret = sny::to_string(-1234);
	return 0;
}

这里注意,本来调用to_string函数最后返回值的时候,str到ret应该是两次拷贝----str出了作用域要销毁,所以会产生一个临时变量,然后再用这个变量拷贝给ret。但是编译器对于这种连续的拷贝作了优化,所以最后只会有一次拷贝。

在这里插入图片描述

另外,一定是连续的拷贝才会被优化,否则不会优化,如下:
在这里插入图片描述
注意,这里出现三次而不是两次拷贝构造,是因为上面代码中的拷贝构造使用的是现代写法实现的,里面多了一次拷贝。

但是,就算是作了优化,但最后还是有一次拷贝,C++11的解决方法是什么呢?

答案是移动构造:

//移动构造
string(string&& s)
{
	cout << "string(string&& s) -- 移动拷贝" << endl;
	swap(s);
}

这段代码要放在上面的string类中。
在这里插入图片描述
这里再补充一点:

C++11中的右值又分为纯右值和将亡值。
纯右值一般指的是内置类型表达式的值;将亡值一般指的是自定义类型表达式的值。

顾名思义,将亡值就是快要嗝儿屁的值。既然都要嗝儿屁了,就没必要再将其内容拷贝一遍了,直接将其抢过来就行。(但这里的抢不是单纯地抢,而是将自己和它做交换)

所以,上面例子中,编译器做出的优化可以分为两部分----两次连续的构造合并为一次,以及返回的str被识别为右值。所以,就调用了移动构造。

但是这个东西要慎用,因为自己的东西也会被换给它,然后被它一起带到坟墓里。如下:
在这里插入图片描述

同样的,赋值构造也可以采用类似的做法:

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

注意!!!右值引用的工作机制是借助于移动语义(移动构造或移动赋值),进行资源转移,而不是延长将亡值的生命周期!(有一些文章中说延长生命周期毫无疑问是错的)

5.4 万能引用

虽然右值引用很厉害,但是左值引用只能引用左值,右值引用只能引用右值。当两个变量/类型需要完成相同的功能,但差别仅仅是一个是左值,一个是右值时,就不得不写两个版本的功能函数,造成了代码冗余。

所以,C++11又增加了一个万能引用,可以接收左值和右值,如下:

template<typename T>
void PerfectForward(T&& t)
{
	//
}
int main()
{
	PerfectForward(10);// 右值
	int a;
	PerfectForward(a);// 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b);// const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

在这里插入图片描述

但是注意,如果传过去的参数属性为const,则模板内不能对其更改;若没有const属性的参数,则可以修改,如下:
在这里插入图片描述

在这里插入图片描述

5.5 完美转发

如果对于上面的模板中接收到的值,再一次传参,会怎么样呢?

首先,铺垫一个小知识点,任何右值引用的本质都是左值,比如:

int&& rr=10;

rr本身就是一个左值。因为10作为一个常量本来没有为其准备存储的地址,但是一旦被右值引用,就必须在内存中为它开一个空间进行存储,这时rr就成了左值

所以,在上面的代码中,所有的t本质都是左值,如下:
在这里插入图片描述
如果将其move一下,就全都变成右值了。那到底如何将其以原来的属性传参呢?

这个时候就要用到完美转发了,它可以发在传参的过程中保留对象原生类型属性,如下:
在这里插入图片描述
完美转发原理如下:

//完美转发原型
T&amp;&amp; forward(T&amp;&amp; t) { return static_cast<T&amp;&amp;>(t); }    

// 用法:   template<typename T>  
void func1(T &amp;&amp; val) { func2(std::forward<T>(val)); }  

// 当传入左值引用   
void func1(T&amp; &amp;&amp; val) { func2(static_cast<T&amp; &amp;&amp;>(val)); }  
// 引用折叠后:   
void func1(T&amp; val) { func2(static_cast<T&amp;>(val)); }   
 
// 当传入右值引用   
void func1(T&amp;&amp; &amp;&amp; val) { func2(static_cast<T&amp;&amp; &amp;&amp;>(val)); } 
// 引用折叠后: 
void func1(T&amp;&amp; val) { func2(static_cast<T&amp;&amp;>(val)); }     

本篇完,青山不改,绿水长流!

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

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

相关文章

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表&#x1f60e;前言&#x1f64c;复制带随机指针的链表&#x1f64c;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵…

shell结构化命令中for命令

shell脚本编程系列 for var in list docommands done读取列表中的值 每次遍历值列表时&#xff0c;for命令会将列表中的下一个值赋值给变量 #!/bin/bash# basic for commandfor test in Alabama Alaska Arizona Arkansas California Coloradodoecho The next state is $testdo…

第07章_面向对象编程(进阶)

第07章_面向对象编程(进阶) 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 1. 关键字&#xff1a;this 1.1 this是什么&#xff1f; 在Java中&#xff0c;this关键字不算难理解…

关于图像分割的预处理 transform

目录 1. 介绍 2. 关于分割中的 resize 问题 3. 分割的 transform 3.1 随机缩放 RandomResize 3.2 随机水平翻转 RandomHorizontalFlip 3.3 随机竖直翻转 RandomVerticalFlip 3.4 中心裁剪 RandomCrop 3.5 ToTensor 3.6 normalization 3.7 Compose 4. 预处理结果可视…

WPF mvvm框架Stylet使用教程-基础用法

Stylet框架基础用法 安装Nuget包 在“管理Nuget程序包”中搜索Stylet&#xff0c;查看Stylet包支持的net版本&#xff0c;然后选择第二个Stylet.Start包进行安装&#xff0c;该包会自动安装stylet并且生成基本的配置 注意事项&#xff1a;安装时要把需要安装的程序设为启动项…

第06章_面向对象编程(基础)

第06章_面向对象编程&#xff08;基础&#xff09; 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 学习面向对象内容的三条主线 Java类及类的成员&#xff1a;&#xff08;重点&…

《QT+CGAL网格处理——网格重建》

QT+CGAL网格处理——网格重建 一、重建效果二、代码分析显示代码格式转换彩色网格显示三、后续一、重建效果 二、代码分析 显示 1、依旧采取VTK显示,参照《QT+PCL》; 2、点数据、网格数据依旧采用pcl数据结构,cgal处理完成后转换格式即可 界面参照:

微服务学习-SpringCloud -Nacos (心跳机制及健康检查源码学习)

文章目录心跳机制与健康检查流程图心跳机制与健康检查总结详细源码说明当多个服务进行注册时&#xff0c;如何解决注册表并发冲突问题?心跳机制与健康检查流程图 心跳机制与健康检查总结 微服务在启动注册Nacos时&#xff0c;会创建一个定时任务&#xff0c;定时向服务端发生…

基于文心一言的底层视觉理解,百度网盘把「猫」换成了「黄色的猫」

随着移动互联网的一路狂飙&#xff0c;手机已经成为人们的新器官。出门不带钥匙可以&#xff0c;不带手机却是万万不可以的。而手机上&#xff0c;小小的摄像头也越来越成为各位「vlogger」的口袋魔方。每天有超过数亿的照片和视频被上传到百度网盘中&#xff0c;这些照片和视频…

Nginx 实战-负载均衡

一、负载均衡今天学习一下Nginx的负载均衡。由于传统软件建构的局限性&#xff0c;加上一台服务器处理能里的有限性&#xff0c;在如今高并发、业务复杂的场景下很难达到咱们的要求。但是若将很多台这样的服务器通过某种方式组成一个整体&#xff0c;并且将所有的请求平均的分配…

Kafka的命令行操作

一、topic命令 下面Windows命令需要把cmd路径切换到bin/windows下。 而Linux命令只需要在控制台切换到bin目录下即可。 下面都以Windows下的操作为例&#xff0c;在Linux下也是一样的。 1.1 查看主题命令的参数 kafka-topics.bat # Windows kafka-topics.sh # Linux输…

机器学习中的数学——学习曲线如何区别欠拟合与过拟合

通过这篇博客&#xff0c;你将清晰的明白什么是如何区别欠拟合与过拟合。这个专栏名为白话机器学习中数学学习笔记&#xff0c;主要是用来分享一下我在 机器学习中的学习笔记及一些感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎私信或者评论区留言&…

关于人工智能前沿信息获取的精品课程

通过观看在线课程可以很方便掌握获取人工智能前沿信息的方法&#xff0c;本文将介绍一些相关课程。 1. 武汉大学黄如花教授的信息检索中文课程知识面广泛&#xff0c;内容详尽&#xff0c;讲解清晰&#xff0c;课程的视频见课程 56。 ​ 课程 56 武汉大学黄如花教授的《信息检…

图神经网络GNN介绍

目录标题图神经网络基础图基本模块定义图的邻接矩阵点特征的更新&#xff08;重构&#xff09;多层GNN图卷积GCN模型GCN基本思想网络层数&#xff1a;基本计算图注意力机制graph attention networkT-GCN序列图神经网络图相似度图神经网络基础 图基本模块定义 三个特征&#x…

C++ 模板初阶

目录 一、函数模板 1.函数模板的概念 2.函数模板的定义 3.函数模板的原理 4.函数模板的实例化 ①隐式实例化 ②显式实例化 5.非模板函数与同名的函数模板同时存在 6.模板参数的匹配原则 二、类模板 1.类模板的定义格式 2.采用类模板的类外函数的定义格式 3.类模板…

【Linux-计算机网络】-TCP协议通信流程

1.TCP协议通信流程图 1.1TCP协议的通讯流程可以分为以下步骤&#xff1a; 应用层&#xff1a;应用程序通过系统调用API&#xff08;如socket&#xff09;创建一个TCP套接字&#xff08;socket&#xff09;&#xff0c;并设置好相关的选项。 传输层&#xff1a;当应用程序调用c…

机器学习中的数学原理——过拟合、正则化与惩罚函数

通过这篇博客&#xff0c;你将清晰的明白什么是过拟合、正则化、惩罚函数。这个专栏名为白话机器学习中数学学习笔记&#xff0c;主要是用来分享一下我在 机器学习中的学习笔记及一些感悟&#xff0c;也希望对你的学习有帮助哦&#xff01;感兴趣的小伙伴欢迎私信或者评论区留言…

153.网络安全渗透测试—[Cobalt Strike系列]—[生成hta/exe/宏后门]

我认为&#xff0c;无论是学习安全还是从事安全的人多多少少都会有些许的情怀和使命感&#xff01;&#xff01;&#xff01; 文章目录一、后门简介1、hta后门2、exe后门3、宏病毒后门二、生成后门并测试0、测试环境1、生成hta后门并测试2、生成exe后门并测试3、生成宏病毒后门…

分布式监控平台-Zabbix

分布监控平台-Zabbix一、Zabbix概述1、Zabbix是什么&#xff1f;2、Zabbix监控原理二、部署Zabbix服务端&#xff08;端口&#xff1a;10051&#xff09;1、关闭防火墙 修改主机名2、获取Zabbix下载资源3、安装SCL(Software Collections)&#xff0c;修改 Zabbix-front 前端源4…

SQL Server的子查询

SQL Server的子查询一、子查询基础知识二、子查询规则三、限定子查询中的列名四、子查询的多层嵌套五、相关子查询六、子查询类型总结一、子查询基础知识 子查询是嵌套在SELECT、INSERT、UPDATE、DELETE语句中或另一个子查询中的查询。 可以在允许表达式的任何位置使用子查询。…