【C++进阶之路】C++11(上)

news2024/11/25 14:49:11

文章目录

  • 一、列表初始化
    • 1.{}
    • 2.initializer_list
  • 二、声明
    • 1.auto
    • 2.deltype
  • 三、右值与左值
    • 1.基本概念
    • 2.应用场景
      • 1.左值引用
      • 2.右值引用
      • 3.完美转发
      • 4.万能引用
  • 四、新增默认成员函数
  • 五、lambda表达式
    • 1.基本语法
      • 1.1捕捉列表
      • 1.2参数列表
      • 1.3返回类型
      • 1.4函数体
    • 2.底层原理
  • 总结

一、列表初始化

1.{}

  • 内置类型
	int a1{ 1 };
	int a2 = { 2 };
  • 自定义类型
	class Point
	{
	public:
		   Point(int a,int b)
			:_a(a)
			,_b(b)
		{}
	private:
		int _a;
		int _b;
	};

	int main()
	{
	
		//数组
		int arr1[]{ 1,2,3 };//列表初始化
		int arr2[] = { 1,2 };//数组初始化。
		
		//自定义类型初始化
		Point p1{ 1,2 };//这里是显示调用构造函数
	
		Point p2 = { 1,2 };
		//本质上是C++11支持多参数的隐式类型转换,编译器优化为直接构造。
		//验证:在构造函数前面+explicit 语法会报错。
	
		const Point& p4 = { 1,2 };
		//多参数的隐式类型转换,生成一个匿名对象其具有常性,不可被修改。
	
		int* ptr1 = new int[] {1, 2, 3};
		Point* ptr2 = new Point[]{{1,2},{1,3}};
		//外面的一层为数组,里面的为列表初始化。
		
		return 0;
	}

总结:

  • 一切即可用{}进行初始化。
  • {}支持多参数的隐式类型转换,也用于单参数的隐式类型转换。
  • 对于自定义类型初始化不加 = 直接跟 {}——显示调用构造函数。
  • 对于加 = 再跟{}初始化——隐式类型转换+编译器的优化 - > 调用构造函数(构造函数前加explicit验证)。

2.initializer_list

  • initializer_list是编译器特殊支持的内置容器,不可单独实现,是一个常量数组。
    在这里插入图片描述
    说明:这是库里initializer_list的所有接口。
	int main()
	{
		const char* str = "abcd";//常量区字符串。返回的是首字母的地址。
		int arr[] = {1,2,3,4};//直接进行赋值。
		initializer_list<int> lt;//此处是默认构造函数进行初始化
		initializer_list<int> lt = { 1,2,3,4 };
		//常量区数组,此处存于栈区。
		//此处是编译器直接进行识别进行初始化,可以理解是直接内存赋值,
		//与arr数组的赋值雷同。
		return 0;
	}
  • 常见用途:作为构造函数的参数进行初始化/赋值
    在这里插入图片描述

  • 注意

	vector<int> v = { 1,2,3,4 };
	//隐式类型转换 + 编译器优化,调用initializer_list的构造函数进行初始化。
	Point p = { 1,2 };
	//隐式类型转换+编译器优化,调用相应的构造函数进行初始化。
  • 接口实现
	vector(initializer_list<T>& lt)
	{
		reserve(lt.size());
		for(auto e : lt)
		{
			push_back(e);
		}
		//或者直接调用相应的迭代器区间进行初始化,不过得用现代写法再进行交换。
		//注意:构造函数是不能够显示进行调用的!
		//vector tmp(lt.begin(), lt.end());
		//swap(tmp);
	}

其它的接口类似。

二、声明

1.auto

详见:初始C++语法

举例:

int main()
{
	auto p1 = malloc;
	auto p2 = Point(1, 2);
	auto p3 = { 1,2,3 };
	auto p4 = "123";
	auto p5 = 1;
	auto str = string();

	cout << typeid(p1).name() << endl;
	cout << typeid(p2).name() << endl;
	cout << typeid(p3).name() << endl;
	cout << typeid(p4).name() << endl;
	cout << typeid(p5).name() << endl;
	cout << typeid(str).name() << endl;

	cout << endl;
	//typeid().name的返回值的类型为char const* ,即指向的内容不可被修改。
	cout << typeid(typeid(p1).name()).name() << endl;

	return 0;
}

补充:auto是在编译期间进行推导得出类型的,且右边必须有可推导的值,因此不可对类型进行声明。

2.deltype

  • 可得出类型,且可以当做类型名进行使用。
template<class T>
class Func
{
	T _a;
};
int main()
{
	auto p1 = malloc;
	decltype(p1) p2;
	Func<decltype(p2)> p3;
	return 0;
 }
  • 总结
  1. 推导出的类型可以进行声明。
  2. 推导出的类型可以当做函数参数。

用途:一些迭代器类型的声明,以及复杂指针的声明。

三、右值与左值

1.基本概念

  • 右值:不能取地址,一般把内置类型的右值叫做纯右值,自定义类型的右值叫将亡值。
  • 左值:能取地址。
  • 左值引用:对左值起别名。
  • 右值引用:对右值起别名。
  • 引用:不管左值引用还是右值引用,都是对值起别名。

  • 常见右值
int add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 0;
	int b = 0;
	//常见的右值
	//表达式
	a + b;
	//函数的返回值
	add(a, b);
	//常量
	10;
	//验证:不能取地址。
	//cout << &10 << &add(a, b) << &(a + b) << endl;
	return 0;
}

特殊的左值:

int main()
{
	//特殊的左值。
	"abc";//返回的是首字符a的地址。
	const char* p = "abc";
	p[1];//这也是左值。
	return 0;
}

  • 左值引用
int main()
{
	int a = 0;
	int b = 0;

	//左值引用直接对左值取别名
	int& ref1 = a;
	int& ref2 = b;

	//const 左值引用可以引用右值
	const int& ref3 = a + b;
	const int& ref4 = 10;
	
	return 0;
}

拓展:

	const int& ref3 = 10;
	int* ptr = (int*)&ref3;
	*ptr = 100;
	cout << &ref3 << endl;
	cout << ref3 << endl;
	//const左值引用引用右值,开空间存储右值,对const左值引用可以取地址,因此const左值引用为左值。

  • 右值引用
int main()
{
	int a = 0, b = 0;
	int&& ref5 = 10;
	int&& ref6 =  a + b;
	return 0;
}
  • 拓展
int main()
{
	int a = 0;
	int&& ref = move(a);
	//cout << &move(a) << endl;
	//右值引用可以引用move之后的左值,move之后的结果为右值。
	int&& ref1 = 10;
	cout << &ref1 << endl;
	//右值引用能取地址,说明右值引用为左值,对10开空间进行拷贝。
	
	ref1++;
	//右值引用可以进行修改,更说明其为左值。
	
	int&& ref2 = forward<T>(ref1);//叫做完美转发,即保持右值属性,此处了解即可。
	int&& ref3 = move(ref2);//将左值ref2转换为右值。
	//说明:右值引用也可作为右值,需要进行处理。
	return 0;
}

2.应用场景

1.左值引用

  1. 作为函数参数
  2. 作为返回值,前提是引用的值在出作用域不被销毁。
  • 起到的作用都是减少拷贝。

缺陷:当返回一个局部的自定义的很大的对象时,只能用传值返回,不能用传引用返回。

说明:写一份string方便查看细节:

namespace MY_STL
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			_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;
			_size = s._size;
			_capacity = s._capacity;
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		// 赋值重载
		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;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}
  • 举例:
MY_STL::string func()
{
	MY_STL::string str("xxxxxxxxxxxxxxxxxx");

	return str;
}

int main()
{
	//1.连续的拷贝构造进行优化。
	MY_STL::string str = func();

	//2.不连续的拷贝构造,进行逐步拷贝。
	MY_STL::string str1;
	str1 = func();

	return 0;
}

1.图解
在这里插入图片描述

说明:编译器优化的结果是——在return之前就调用拷贝构造进行构造,因为return后就调用析构函数对空间进行销毁了。

2.图解
在这里插入图片描述

运行结果图解:

在这里插入图片描述

2.右值引用

 前面我们举了左值引用的缺陷,那右值引用是来弥补这一个缺陷的,如何弥补呢?接着继续分析。

第一个问题:

void func(const int& a)
{
	cout << "func(const int& a)" << endl;
}
void func(int && a)
{
	cout << "func(int && a)" << endl;
}
int main()
{
	int a = 0;
	int b = 0;

	func(a);
	func(a + b);

	return 0;
}

运行结果如何?构成重载么?如果构成,那是否存在歧义?

  • 运行结果
    在这里插入图片描述
    结果很显然构成重载,且不存在歧义,函数调用会走最匹配的。

接着用上面的例子:

MY_STL::string func()
{
	MY_STL::string str("xxxxxxxxxxxxxxxxxx");

	return str;
}

int main()
{
	MY_STL::string str1;
	str1 = func();
	return 0;
}

 根据前面的思路分析,返回str时发生了一次拷贝构造,生成了匿名对象(右值),且其声明周期只在这一行,那么我们是否可以利用这一特性写出个右值引用版本的赋值呢?那关键就在于这是右值且声明周期只在当前一行,因此我们可以直接换资源,我们顺走匿名对象的资源,之后让匿名对象带走不用的资源(析构函数)

根据这样我们写出来的赋值重载就是这样的:

void swap(string& s)
{
	::swap(_str, s._str);
	::swap(_size, s._size);
	::swap(_capacity, s._capacity);
}
string& operator=(string&& s)
{
	cout << "string& operator=(string&& s) -- 移动赋值" << endl;
	swap(s);
	return *this;
}
  • 补充:右值引用可以被修改,且默认识别为左值。

我们再来运行一下:

在这里插入图片描述

 对比之前,这里就优化了一次深拷贝,在一定程度上提升了效率。


继续使用上面的例子再进行分析:

MY_STL::string func()
{
	MY_STL::string str("xxxxxxxxxxxxxxxxxx");

	return str;
}

int main()
{
	//1.连续的拷贝构造进行优化。
	MY_STL::string str = func();
	return 0;
}

 按理来说这里本该为拷贝构造生成匿名对象,再用匿名对象拷贝构造对象,但编译器看不惯连续的拷贝构造,因此直接优化为了一次拷贝构造,这里如何用右值进行优化呢?编译器直接将返回的str特殊处理成右值,然后用识别之后的右值,进行拷贝构造,那如何写拷贝构造呢?其实跟赋值一样。

string(string&& s)
	:_str(nullptr)
{
	cout << "string(string&& s)——移动拷贝" << endl;
	swap(s);
}

运行结果:
在这里插入图片描述
看结果,确实是编译器对返回值进行了优化,那为啥要进行这样的优化呢?第一点博主认为这样代码具有可移植性,第二点是不用再对之前的代码进行修改即在返回值处修改成move(str)。

此时我们再看之前的那个例子:

MY_STL::string func()
{
	MY_STL::string str("xxxxxxxxxxxxxxxxxx");

	return str;
}

int main()
{
	//1.连续的拷贝构造进行优化。
	MY_STL::string str = func();
	return 0;
}

再看运行结果:

在这里插入图片描述
这里编译器也是做了优化的,对返回值直接识别成右值,直接将两次拷贝构造,优化为两次移动构造,这样处理对一些很大的自定义的值返回,带来了效率的极大的提升!并且写起来也变的更加轻松。


除此之外为了更好更深刻的理解问题,博主举一个例子:

int main()
{
	const string& str = string("xxxxx");
	string("xxxxx").push_back('a');
	return 0;
}

这会引出来几个问题:

  1. 匿名对象具有常属性不可以被修改,但是这里却可以调用接口被修改。
  2. 匿名对象左值引用只能用const左值引用,之后就不可调用接口修改,因为是this指针被const修饰,这一点很容易理解。
  3. 匿名对象右值引用可以被修改,且修改的是匿名对象本身,而对内置类型则是修改其拷贝。

 可以看出匿名对象确实是右值或者说是常量(const左值引用),但也确实可以被修改,其原因就在于这是C++委员会开的绿灯,其目的就在于通过右值引用修改其内部成员而完成资源的转换,进而实现移动赋值(语义)和移动拷贝(语义),而对内置类型的右值进行引用则是开空间存储(消耗不大),因此我们可以把右值引用的最终目的就是为了减少资源的拷贝进行提升效率。

总结一下关键:

  1. 对于内置类型的右值可以认为是常量绝对不可以被修改,而自定义类型的右值也认为是常量,但为了达到目的,开了绿灯因此可以被修改。
  2. 右值引用对于内置类型开空间拷贝,对自定义类型直接取别名,且右值引用之后的别名可以进行修改,且被识别为左值(方便修改),进而方便转资源,提升效率。

 那右值引用是否具有右值属性呢?答案是肯定的。在有些情况下,是需要保持右值属性的。

比如:当我们在vector中push_back一个string。
在这里插入图片描述

int main()
{
	MY_STL::vector < MY_STL::string> v;
	v.push_back("xxxxx");//尾插一个匿名对象的string。
	return 0;
}

因此push_back可优化成这样:

在这里插入图片描述
 但是又涉及一个问题,虽然这里是右值引用,但这里会被识别为左值,进而调用赋值重载跟上面的一样,这要怎么办呢?也就是如何保持其右值的属性呢?

3.完美转发

 完美转发就是来解决上面的问题,右值引用保持其右值的属性,左值引用保持其左值的属性。
 那么最终的push_back就是:

void push_back(value_type&& val)
{
	if (_finish == _end_of_storage)
	{
		size_t new_capacity = size() == 0 ? 4 : \
		capacity() * 2;
		//扩容
		reserve(new_capacity);
	}
	*(_finish++) = forward<value_type>(val);//这样就叫做完美转发。
}
  • 这样就能在插入数据时进一步优化效率。

4.万能引用

 最后再简单的提及一个比较省力的小语法。

void Fun(int& x) 
{ 
	cout << "左值引用" << endl;
}
void Fun(const int& x) 
{ 
	cout << "const 左值引用" << endl; 
}
void Fun(int&& x) 
{
	cout << "右值引用" << endl; 
}
void Fun(const int&& x) 
{
	cout << "const 右值引用" << endl; 
}
template<typename T>
void PerfectForward(T&& t)
{
	Fun(forward<T>(t));
}
int main()
{
	PerfectForward(10); // 右值
	int a;
	PerfectForward(a); // 左值
	PerfectForward(move(a)); // 右值
	const int b = 8;
	PerfectForward(b); // const 左值
	PerfectForward(move(b)); // const 右值
	return 0;
}

运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

四、新增默认成员函数

 C++11新增了两个默认成员函数,不过需要具备以下条件才会自动生成。

  • 你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个
  • 功能:默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造

例:

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
private:
	MY_STL::string _name;
	int _age;
};
int main()
{
	Person s2;
	Person s3("xxxxx",10);
	s3 = move(s2);
	return 0;
}

 分析为什么要这样设计?关键在于默认生成的移动构造函数对内置类型是只拷贝字节的。也就是说我们在类内部定义一个指针,用个指针指向堆空间的资源,默认生成只完成浅拷贝,而不完成资源的交换!并且因为默认生成的析构函数不会对内置类型处理,因此还会造成内存泄漏 , 这就要求我们自己实现析构函数,但是写了析构函数就不会生成默认的移动构造了,这就又反过来要求我们自己写移动构造交换资源。
 这样编译器选择生成的目的在于只要你写了实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个就会默认认为有需要深拷贝的内置成员,因此不会生成只会对内置类型完成按字节拷贝的默认移动构造。

  • 对于移动赋值重载同理。

五、lambda表达式

 lambda其实就是一个对象,有类似函数,可以在语句里面定义即使用。

在之前我们还学过函数指针和仿函数与之相同。

1.基本语法

基本语法:

[capture-list] (parameters) mutable -> return-type { statement}
//捕捉列表          参数      控制属性     返回类型        实现语句

先举个例子看看如何使用:

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };

	auto CompareNameLess = [](const Goods& x1, const Goods& x2)->bool 
	{ return x1._name < x2._name; };
	auto CompareNameGreater = [](const Goods& x1, const Goods& x2)->bool
	{ return x1._name > x2._name; };

	sort(v.begin(), v.end(), CompareNameLess);
	sort(v.begin(), v.end(), CompareNameGreater);
	//也可以直接这样写:
	sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool 
	{ return x1._name < x2._name; }
	);
	sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool 
	{ return x1._name > x2._name; }
	);
	
	//箭头以及后面的类型可省略,编译器会自动进行推导。
	sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)
	{ return x1._name > x2._name; }
	);
	return 0;
}

 这样使用其实有一定的好处,就是避免了有人使用仿函数或者函数指针时,定义的类或者函数名字不规范而导致代码的可读性极差,还要回去找定义才能进行理解, 花费的时间成本很大。

再来谈谈其具体的几个部分:

1.1捕捉列表

 捕捉列表其实是用所在局部域的变量,因为其实lambda就是一个类的匿名对象,其定义的内容在底层是一个函数,又因为函数与函数之间的域是独立的,不可互相使用变量只能通过传参的形式或者使用全局变量的方式间接使用,这里的捕捉列表就是为了间接使用不同域之间的变量而诞生的。

  • 捕捉值
  1. 捕捉指定值。
int main()
{
	int x = 1, y = 2;
	auto tmp = [x, y]() 
	{ 
		//x = 0;并且此处是const的左值,不可被修改。
		return x + y; 
	};
	return 0;
}

 这里的x与y进行值捕捉是加const的,因此不可被修改,如果非要修改呢?

 很简单:在()后加 mutable。

这样就可以被修改了:

int main()
{
	int x = 1, y = 2;
	auto tmp = [x, y]() mutable
	{ 
		x = 0;
		return x + y; 
	};
	return 0;
}

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

class Person {
public:

    void modify()const
    {
        //const函数
        //比如int float等类型,其值不可修改。
        //指针的值不可被修改,内容是可以被修改的。
        //引用底层是指针,其指向的内容是可以被修改的。
        //static静态对象不受this指针的约束也可以被修改。

        //age++;
        //name = "aaaa";

        nums++;
        *name;
        ref = 0;
        _id = 0;
    }

private:
    int age;
    int& ref = age;
    char* name;
    mutable int nums;
    static int _id;
};
int Person::_id = 0;
  1. 捕捉所有值(如果有this则this也算进去)
int main()
{
	int x = 1, y = 2;
	auto tmp = [=]() mutable
	{ 
		x = 0;
		return x + y; 
	};
	return 0;
}

  • 捕捉引用
  1. 捕捉指定引用
int main()
{
	int x = 1,y = 2;
	auto tmp = [&x,&y]()
	{ 
		return x + y; 
	};
	return 0;
}
  1. 捕获所有引用(如果有this包含this)
int main()
{
	int x = 1,y = 2;
	auto tmp = [&]()
	{ 
		return x + y; 
	};
	return 0;
}

注意

  1. 捕捉列表不允许变量重复传递,否则就会导致编译错误,比如x以值的形式捕捉过一回就不能用引用再进行捕获。
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b。
  3. 在全局定义lambda表达式,其捕捉列表必须为空。
  4. lambda只能捕捉其所在局部作用域的变量。
  5. lambda之间不能能进行赋值,因为类型不同。

1.2参数列表

  • 与普通的函数列表一样。

1.3返回类型

  • 通常可通过实现体进行推导,所以一般情况下可以省略。

1.4函数体

  • 写函数的实现功能。

2.底层原理

  1. 类型
int main()
{

	auto tmp = []() {};
	cout << typeid(tmp).name() << endl;
	return 0;
}

运行结果:
在这里插入图片描述

可以看出其是一个类,至于这么长,博主猜测是采用了哈希函数的那套方法进行映射出了一段关键码,由此产生不同的类,这也间接说明了不同lambda表达式的类型各不相同。

  1. 反汇编
struct tmp1
{
	bool operator()()
	{
		return 0;
	}
};
int main()
{
	auto tmp = []()->bool { return 0; };
	tmp();
	tmp1()();
	return 0;
}

反汇编截图:
在这里插入图片描述

  • 因此:底层是仿函数。

总结

 今天的分享到此就结束了,下篇文章再见咯,如果有所帮助,不妨点个赞鼓励一下吧!

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

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

相关文章

GPT系列模型解读:GPT-1

GPT系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一系列基于Transformer架构的预训练语言模型&#xff0c;由OpenAI开发。以下是GPT系列的主要模型&#xff1a; GPT&#xff1a;GPT-1是于2018年发布的第一个版本&#xff0c;它使用了12个Transformer…

2021-06-20 51单片机基于STC89C52RC的简易秒表的设计与实现(外部中断1和2)

缘由基于STC89C52RC的简易秒表的设计与实现_编程语言-CSDN问答 1.功能要求&#xff1a; K1键做启动停止秒表&#xff08;外部中断0&#xff09;&#xff0c;K2键做秒表归零&#xff08;外部中断1&#xff09;&#xff0c;4位数码管动态扫描显示&#xff0c;定时范围改成0到00…

房产政策松绑,VR看房助力市场回春

近日房贷利率、房产限购开始松绑&#xff0c;房地产市场逐渐被激活&#xff0c;房产行业的线上服务能力&#xff0c;也愈发的受到了重视。随着房贷利率、首付比例变化的消息逐渐推出&#xff0c;部分用户开始入手房产市场&#xff0c;因此房产行业的线上服务也需要不断升级&…

【计算机组成原理】读书笔记第五期:通过汇编语言了解程序的实际构成

目录 写在开头 汇编语言和本地代码的关系 汇编语言的源代码 伪指令 汇编的基本语法 常见的汇编指令 mov push和pop 函数的使用机制 函数的调用 函数参数的传递与返回值 全局变量 局部变量 程序的流程控制 循环语句 条件分支 通过汇编语言了解程序运行方式的必…

RTP/RTCP 协议讲解

文章目录 前言一、RTP 协议1、RTP 协议概述2、RTP 工作机制3、RTP 协议的报文结构4、wireshark 抓取 RTP 报文 二、RTCP 协议1、RTCP 协议概述2、RTCP 工作机制3、RTCP 数据报4、wireshark 抓取 RTCP 报文 三、RTSP 和 RTP 的关系四、易混淆概念1、RTP over UDP 和 RTP over RT…

typescript 泛型详解

typescript 泛型 泛型是可以在保证类型安全前提下&#xff0c;让函数等与多种类型一起工作&#xff0c;从而实现复用&#xff0c;常用于: 函数、接口、class中。 需求:创建一个id 函数&#xff0c;传入什么数据就返回该数据本身(也就是说&#xff0c;参数和返回值类型相同)。 …

从 0 到 1 ,手把手教你编写《消息队列》项目(Java实现) —— 创建项目 / 创建核心类

文章目录 一、创建SpringBoot项目二、创建核心类创建 Exchange类创建 MSGQueue类创建 Binding类创建Message类 一、创建SpringBoot项目 在项目中添加这四个依赖! 二、创建核心类 交换机 :Exchange 队列 :Queue 绑定关系: Binding 消息 :Message 这些核心类都存在于 BrokerSe…

常见加密和解密方法介绍。

介绍常见的加密和解密方法。 加密是利用数学方法将明文转化为密文&#xff0c;从而达到保护数据的目的。 通过加密可保证数据的机密性、完整性、鉴别性。 机密性&#xff1a;通过数据加密实现。只允许特定用户访问和阅读信息。 完整性&#xff1a;通过数字加密、散列、数字签名…

【生物信息学】使用皮尔逊相关系数进行相关性分析

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 3. IDE 三、实验内容 0. 导入必要的工具 1. cal_pearson&#xff08;计算皮尔逊相关系数&#xff09; 2. 主程序 a. 实验1&#xff08;较强的正相关关系&#xff09;&#xff1a; b. 实验2&#xff0…

计算机网络学习易错点(持续更新~~~)

目录 概述 1.internet和Internet的区别 2.面向连接和无连接 3.不同的T 4.传输速率和传播速率 5.传播时延和传输时延&#xff08;发送时延&#xff09; 6.语法&#xff0c;语义和同步 一.物理层 1.传输媒体与物理层 2.同步通信和异步通信 3.位同步&#xff08;比特同…

leetCode 53.最大子数和 图解 + 贪心算法/动态规划+优化

53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入…

C++面试题准备

文章目录 一、线程1.什么是进程&#xff0c;线程&#xff0c;彼此有什么区别?2.多进程、多线程的优缺点3.什么时候用进程&#xff0c;什么时候用线程4.多进程、多线程同步&#xff08;通讯&#xff09;的方法5.父进程、子进程的关系以及区别6.什么是进程上下文、中断上下文7.一…

FFMPEG 视频类过滤器学习整理

addroi 作用 在视频帧上标记一块感兴趣的区域。 帧数据被原封不动地传递&#xff0c;但元数据被附加到帧&#xff0c;指示可能影响后续编码行为的感兴趣区域。可以通过多次应用过滤器来标记多个区域。 参数 qoffset: 应用在此区域的量化偏移。 参数范围&#xff1a;-1 ~ …

笔记一:odoo透视表和图表

透视表 1、首先在xml文件添加pivot 说明&#xff1a;&#xff08;1&#xff09;根元素pivot中属性&#xff1a; disable_linking&#xff1a;设置为True&#xff0c;删除表格单元格到列表视图的链接 display_quantity&#xff1a;设置为True&#xff0c;默认显示“数量”列 d…

什么是FOSS

FOSS 是指 自由和开放源码软件(Free and Open Source Software)。这并不意味着软件是免费的。它意味着软件的源代码是开放的&#xff0c;任何人都可以自由使用、研究和修改代码。这个原则允许人们像一个社区一样为软件的开发和改进做出贡献。

Axios post请求出现500错误

笔者在编写前端form表单传后端数据的时候&#xff0c;出现了以下问题 一、问题场景 当我用axios发送post请求的时候&#xff0c;出现了500错误 笔者找了很长时间错误&#xff0c;代码没问题&#xff0c;后端接口也没问题&#xff0c;后来发现问题出在实体类上了 当前端post请…

电脑msvcp140丢失报错解决方法,msvcp140.dll重新安装的解决方法

msvcp140.dll丢失可能会导致一些基于Microsoft Visual C 编写的程序和游戏无法正常运行。msvcp140.dll是Microsoft Visual C Redistributable的一个组件&#xff0c;它包含了 C 运行时库&#xff0c;这些库在运行程序时会被加载到内存中。如果该文件丢失或损坏&#xff0c;程序…

计算机视觉——飞桨深度学习实战-深度学习网络模型

深度学习网络模型的整体架构主要数据集、模型组网以及学习优化过程三部分&#xff0c;本章主要围绕着深度学习网络模型的算法架构、常见模型展开了详细介绍&#xff0c;从经典的深度学习网络模型以CNN、RNN为代表&#xff0c;到为了解决显存不足、实时性不够等问题的轻量化网络…

用于图像恢复的即插即用 ADMM:定点收敛和应用(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【MySQL入门到精通-黑马程序员】MySQL基础篇-DML

文章目录 前言一、DML-介绍二、DML-添加数据三、DML-修改数据四、DML-删除数据总结 前言 本专栏文章为观看黑马程序员《MySQL入门到精通》所做笔记&#xff0c;课程地址在这。如有侵权&#xff0c;立即删除。 一、DML-介绍 DML&#xff08;Data Manipulation Language&#xf…