【C++进阶】C++11新特性上篇(万字详解)

news2024/9/24 7:16:54

🎇C++学习历程:入门


  • 博客主页:一起去看日落吗
  • 持续分享博主的C++学习历程
  • 博主的能力有限,出现错误希望大家不吝赐教
  • 分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记,树🌿成长之前也要扎根,也要在漫长的时光🌞中沉淀养分。静下来想一想,哪有这么多的天赋异禀,那些让你羡慕的优秀的人也都曾默默地翻山越岭🐾。

在这里插入图片描述

♠️ ♥️ ♣️ ♦️

目录

  • ♠️ 1. C++11简介
  • ♠️ 2. 列表初始化
    • ♥️ 2.1 {} 初始化
    • ♥️ 2.2 std::initializer_list
  • ♠️ 3. 声明
    • ♥️ 3.1 auto
    • ♥️ 3.2 decltype
    • ♥️ 3.3 nullptr
  • ♠️ 4. STL中一些变化
  • ♠️ 5. final 和 override
  • ♠️ 6. 右值引用和移动语义
    • ♥️ 6.1 左值引用和右值引用
    • ♥️ 6.2 左值引用和右值引用比较
    • ♥️ 6.3 右值引用使用场景和意义
    • ♥️ 6.4 左值引用使用场景
    • ♥️ 6.4 左值引用短板
    • ♥️ 6.5 右值引用和移动语义解决上述问题
    • ♥️ 6.6 STL容器插入接口函数也增加了右值引用版本

♠️ 1. C++11简介

相比C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率。

♠️ 2. 列表初始化

♥️ 2.1 {} 初始化

  • 在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定。
struct Point
{
 	int _x;
 	int _y;
};
int main()
{
 	int array1[] = { 1, 2, 3, 4, 5 };
 	int array2[5] = { 0 };  //0 0 0 0 0 
 	Point p = { 1, 2 };  //运用了struct结构体的语法     初始化
 	return 0; 
 }

  • C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
struct Point
{
	int _x;
	int _y;

	Point(int x, int y)
		:_x(x)
		, _y(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()
{
	
	Point p = { 1, 2 };
	//Point p{ 1, 2 };
	
	Point* p3 = new Point[3]{ {1, 1}, { 2, 2 }, { 3, 3 } };

	int* p1 = new int(0);
	int* p2 = new int[5]{1,2,3,4,5};  
	//C++11中 new可以初始化数组

	//自定义类型的列表初始化
	Date d1(2022, 3, 13);
	Date d2 = { 2022, 3, 15 };
	Date d3{ 2022, 3, 15 };
	Date{2022,3,15};

	int i = 1;
	int j = { 2 };
	int k{ 3 };
	return 0;
}


♥️ 2.2 std::initializer_list

std::initializer_list介绍文档

  • std::initializer_list是什么类型
int main()
{
	// the type of il is an initializer_list
	auto il = { 10, 20, 30 };
	cout << typeid(il).name() << endl;
	return 0;
}

在这里插入图片描述

  • 模拟实现的vector支持{}初始化和赋值
template<class T>
class vector 
{
public:
	typedef T* iterator;
	vector(initializer_list<T> l)
	{
		_start = new T[l.size()];
		_finish = _start + l.size();
		_endofstorage = _start + l.size();
		iterator vit = _start;
		typename initializer_list<T>::iterator lit = l.begin();
		while (lit != l.end())
		{
			*vit++ = *lit++;
		}
		//for (auto e : l)
		//   *vit++ = 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;
	}
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;
};

  • std::initializer_list使用场景

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

class A
{
public:
	A(int a, double d, int b)
		:_a(a)
		, _d(d)
		, _b(b)
	{}
private:
	int _a;
	double _d;
	int _b;
};
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()
{
	//内置类型的列表初始化
	vector<int> v1 = { 1, 2, 3, 4, 5 };
	vector<int> v2{ 1, 2, 3, 4, 5 };
	//vector (initializer_list<value_type> il,const allocator_type& alloc = allocator_type());


	auto lt1 = {1, 2, 3, 4};
	initializer_list<int> lt2 = { 1, 2, 3, 4 };
	//auto=initializer_list<int>


	map<string, int> dict1 = { pair<string, int>("sort", 1), pair<string, int>("insert", 2) };
	map<string, int> dict2 = { { "sort", 1 }, { "insert", 2 } };
	//首先{ "sort", 1 }, { "insert", 2 }创建成initializer_list<map<string,int>>类型的对象
	//最后map (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
	//这个构造函数初始化dict2


	//自定义类型的列表初始化
	Date d1(2022, 3, 13);
	Date d2 = { 2022, 3, 15 };
	Date d3{ 2022, 3, 15 };
	Date{ 2022, 3, 15 };


	
	A aa1 = { 1, 1.11, 1};
	list<A> lt = { { 1, 1.11, 1}, { 2, 2.22, 1} };
	//首先{ { 1, 1.11, 1}, { 2, 2.22, 1} }创建成initializer_list<list<A>>类型的对象(首先是A类得有相应的构造函数才行)
	//最后list的构造函数初始化it
	return 0;
}


♠️ 3. 声明

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

♥️ 3.1 auto

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

int main()
{
	int i = 10;
	auto p = &i;
	
	auto pf = strcpy;
	//输出p、pf的类型
	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

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

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret = t1 * t2;
	vector<decltype(t1* t2)> v;
	v.push_back(ret);
	cout << typeid(ret).name() << endl;
}

int main()
{
	int i = 10;
	auto p = &i;
	auto pf = strcpy;
	decltype(pf) pf1;    //char * (__cdecl*)(char *,char const *)
	vector<decltype(pf)> v;

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

	return 0;
}

注意:decltype与auto的差别:auto必须要求显式初始化,而decltype没要求


♥️ 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. STL中一些变化

新容器

用橘色圈起来是C++11中的一些几个新容器,但是实际最有用的是unordered_map和
unordered_set。其他的大家了解一下即可。

在这里插入图片描述

容器中的一些新方法

如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。
比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作。


♠️ 5. final 和 override

final

  • final修饰类的时候,表示该类不能被继承
class A final //表示该类是最后一个类
{
private:
	int _year;
};
class B : public A //无法继承
{

};

  • final修饰虚函数时,这个虚函数不能被重写
class A 
{
public:
	virtual void fun() final//修饰虚函数
	{
		cout << "this is A" << endl;
	}
private:
	int _year;
};
class B : public A
{
public:
	virtual void fun()//父类虚函数用final修饰,表示最后一个虚函数,无法重写
	{
		cout << "this is B" << endl;
	}
};


override

  • 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class A 
{
public:
	virtual void fun()
	{
		cout << "this is A" << endl;
	}
private:
	int _year;
};
class B : public A
{
public:
	virtual void fun() override
	{
		cout << "this is B" << endl;
	}
};


♠️ 6. 右值引用和移动语义

♥️ 6.1 左值引用和右值引用

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

  • 左值引用

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

int main()
{
	// 以下的p、b、c、*p都是左值
	int* p = new int(0);
	int b = 1;
	const int c = 2;


	//	可以取地址
	cout << &p << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &(*p) << endl;
	
	b = c;
	
	// 以下几个是对上面左值的左值引用
	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);
	
	//	// 不能取地址
	//	cout << &10 << endl;
	//	cout << &(x+y) << endl;
	//	cout << &fmin(x, y) << endl;
	
	// 这里编译会报错: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;
	int& rr = rr1;
	//rr2 = 5.5;  // 报错
	return 0;
}

注意:左值引用可以连续左值引用,而右值引用是不可以连续右值引用的(右值引用后该变量为左值,不能使用右值引用了)


♥️ 6.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 = move(a);
	return 0;
}


♥️ 6.3 右值引用使用场景和意义

右值引用可以补齐左值引用的短板

namespace lc
{
	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(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;

			//this->swap(s);
			swap(s);
		}

		// 赋值重载
		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
	};
}


♥️ 6.4 左值引用使用场景

void func1(bit::string s)
{}

void func2(const bit::string& s)
{}

int main()
{
	bit::string s1("hello world");
	// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
	func1(s1);
	func2(s1);
	
	// string operator+=(char ch) 传值返回存在深拷贝
	// string& operator+=(char ch) 传左值引用没有拷贝提高了效率
	s1 += '!';
	
	return 0;
}

♥️ 6.4 左值引用短板

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


♥️ 6.5 右值引用和移动语义解决上述问题

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人(将亡值)的资源来构造自己。

在这里插入图片描述
注意:出了作用域,如果返回对象不在了,不能使用引用返回(左值引用和右值引用都不可以)

namespace byih
{
	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)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}

		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			//this->swap(s);
			swap(s);
		}

		// 拷贝赋值
		string& operator=(const string& s)
		{
			cout << "string& operator=(const string& s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

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

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

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};



	string to_string(int val)
	{
		string str;
		while (val)
		{
			int i = val % 10;
			str += ('0' + i);
			val /= 10;
		}
		reverse(str.begin(), str.end());
		return str;
	}
}

在这里插入图片描述

在这里插入图片描述

右值是将亡值而将亡值不一定是右值

注意:

按照语法,右值引用只能引用右值,但右值引用也能引用左值。因为:有些场景下,可能 真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move 函数将左值转化为右值。C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性, 它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。
使用move函数时,要注意该对象的资源(所开空间),该对象的资源会根据移动赋值、移动拷贝来决定,建议使用完move函数之后不要再使用该对象了


♥️ 6.6 STL容器插入接口函数也增加了右值引用版本

void push_back (value_type&& val);
int main()
{
	list<bit::string> lt;
	bit::string s1("1111");
	// 这里调用的是拷贝构造
	lt.push_back(s1);
	// 下面调用都是移动构造
	lt.push_back("2222");
	lt.push_back(std::move(s1));
	return 0;
}

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

在这里插入图片描述


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

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

相关文章

Dubbo 4 Dubbo 高级特性 4.2 Dubbo 常用高级配置 4.2.6 负载均衡

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 4 Dubbo 高级特性 文章目录Dubbo4 Dubbo 高级特性4.2 Dubbo 常用高级配置4.2.6 负载均衡4.2 Dubbo 常用高级配置 4.2.6 负载均衡 【举个栗子】 现在 同一个服务 提供者&#xff0c;我们把它 部署在了…

Dijkstra迪杰斯特拉算法

1.场景 用于计算一个节点到其他节点的最短路径&#xff0c;特点是由其实点位中心向外层扩展&#xff08;BFS思想&#xff09;&#xff0c;直至扩展到终点为止 2.认识 https://blog.csdn.net/weixin_57128596/article/details/126982769?ops_request_misc%257B%2522request%…

自动生成changelog

本文主要记录我如何在React项目中优雅的使用TypeScript&#xff0c;来提高开发效率及项目的健壮性。 项目目录及ts文件划分 由于我在实际项目中大部分是使用umi来进行开发项目&#xff0c;所以使用umi生成的目录来做案例。 . ├── README.md ├── global.d.ts ├── mo…

安全分析模型

安全分析模型自动化调优 MLOps&#xff08;Machine Learning Operations&#xff09;是一种人工智能 的工程实践&#xff0c;是面向机器学习项目的研发运营管理体系 。旨在实现 ML 管道的操作、ML 模型的部署和管理标准化&#xff0c;支持ML 模型的发布、激活、监控、性能跟踪…

Pytorch 学习之:关于 GPU 训练你必须知道的几件事

文章目录torchvision 下载的 pretrain 模型路径cuda 版本一定要适配多 cuda 训练 DataParallel 使用须知torchvision 下载的 pretrain 模型路径 使用 torchvision 来直接下载 pretrained 模型&#xff0c;有时候服务器的下载速度很慢&#xff0c;不如直接下载到本地之后传上去…

简单个人静态HTML网页设计作品 基于HTML+CSS+JavaScript仿小米手机网站 html静态在线购物商城网页制作

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

一种用于模拟电晕放电的高效半拉格朗日算法(Matlab代码实现)

目录 摘要 1 概述 2 数学模型与方法 3 讲解 3.1 测试1 3.2 测试2 3.3 测试3 3.4 测试4 4 Matlab代码实现 摘要 提出了一种无需通量校正的高效电晕放电模拟算法。称为位置-状态分离 (POSS) 方法的算法用于求解电晕放电建模中通常存在的以对流为主的连续性方程。所…

Java项目:ssm实验室预约维修管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为超级管理员、管理员、学生三种角色&#xff0c; 超级管理员角色包含以下功能&#xff1a; 通知管理、用户管理、实验室管理、实验室预…

字节跳动抖音本地生活前端招聘

一、认识Typescript (1)Javascript是一种动态类型的弱类型语言 Javascript超集&#xff1a; A.包含与兼容所有JS特性&#xff0c;支持共存 B.支持渐进式引入与升级 (2)TypeScript是一种静态类型的弱类型语言 静态类型的优点&#xff1a; A.可读性增强&#xff1a;基于语法…

Linux内存分配原理

Linux内存分配原理虚拟内存分区Linux内存分配时的maps文件brk()与sbrk()mmap()与munmap()mmap()munmap()内存分配过程更多资讯、知识&#xff0c;微信公众号搜索&#xff1a;“上官宏竹”。 虚拟内存分区 虚拟内存由于用途不同&#xff0c;分类也不尽相同&#xff0c;一般我们…

底层网络知识详解:从二层到三层-第6讲-交换机与VLAN:办公室太复杂,我要回学校

上一次,我们在宿舍里组建了一个本地的局域网LAN,可以愉快地玩游戏了。这是一个非常简单的场景,因为只有一台交换机,电脑数目很少。今天,让我们切换到一个稍微复杂一点的场景,办公室。 拓扑结构是怎么形成的? 我们常见到的办公室大多是一排排的桌子,每个桌子都有网口,…

C++11标准模板(STL)- 算法(std::minmax_element)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 返回范围内的最小元素和最…

非零基础自学Golang 第11章 文件操作 11.2 文件基本操作 11.2.1 文件创建与打开

非零基础自学Golang 文章目录非零基础自学Golang第11章 文件操作11.2 文件基本操作11.2.1 文件创建与打开第11章 文件操作 11.2 文件基本操作 在学习文件操作之前&#xff0c;我们先来了解一下Linux下的文件权限。 文件有三种权限&#xff0c;分别为读取、写入和执行&#x…

设计模式2 - 创建型模式

23种设计模式分析与见解开篇、UML、软件设计原则https://blog.csdn.net/lili40342/article/details/128358435创建型模式https://blog.csdn.net/lili40342/article/details/128358392结构型模式https://blog.csdn.net/lili40342/article/details/128358313行为型模式https://bl…

【MATLAB100个实用小技巧】——图形处理(67-75)

文章目录前言系列文章67. 图像的块操作68. 图形的过滤操作69. 图像的频率操作70. 函数变换71. RADON 函数变换72. 图像分析&#xff08;1&#xff09;73. 过滤图像74. 图像的区域处理75. 图像的颜色处置前言 &#x1f30f;MATLAB是一个功能强大的软件&#xff0c;我们可以利用M…

Mycat(3):mycat的安装

1、前言 使用mycat要安装JDK.不会的去看Linux里面的安装JDK的知识点&#xff0c;这是不再做说明 也可以直接使用yum install java-1.7.0-openjdk 因为mycat 基于jdk1.7开发的&#xff0c;所有最好安装jdk1.7的版本 重要说明&#xff1a; Mycat-server-1.6-release 版本发布的版…

手机技巧:苹果手机这8个实用小技巧

今天给大家大家分享苹果手机8个实用小技巧&#xff0c;你都会用吗&#xff1f; 1、快速搜索相机照片 相信大家的相册里的照片应该和我一样不说有几千张&#xff0c;几百张总是有的&#xff0c;有时候想找照片&#xff0c;又不想一张一张找怎么办&#xff1f;很简单&#xff0c…

Docker配置从私有仓库拉取镜像

修改Docker配置文件 修改docker的配置文件daemon.json&#xff0c;如果配置文件不存在则直接创建。 vim /etc/docker/daemon.json文件内容如下&#xff0c;其中insecure-registries属性值“registry.luntek-inc.com”代表私有仓库的地址&#xff0c;你需要将registry.luntek-…

JavaSE13-方法

目录 1.方法的基本用法 1.1.什么是方法 1.2.方法定义语法 1.3.方法调用的执行过程 1.4.实参和形参的关系 1.5.方法的返回值 2.方法重载 2.1.方法重载定义 2.2.代码示例 3.方法递归 3.1.方法递归定义 3.2.方法递归使用条件 3.3.递归与非递归优劣比较 3.4.递归执行…

[附源码]Nodejs计算机毕业设计基于网络C++实验管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…