C++11

Posted qnbk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11相关的知识,希望对你有一定的参考价值。

C++11

同一列表初始化

初始化列表时,可以不添加等号

struct Point

	int _x;
	int _y;
;
int main()

	Point p =  1, 2 ;
	Point p 1, 2 ;//可以不使用 '='
	int x2 2 ;
	int* p = new int[4]0;
	int* p1 = new int[4]1, 2, 3, 4;
	return 0;

initializer_list

#include <iostream>
#include <initializer_list>

int main()

	auto li =  10, 20, 30 ;
	initializer_list<int> li2 =  1, 2, 3, 4 ;
	cout << typeid(li).name() << endl;
	return 0;

int main()

	auto li =  10, 20, 30 ;
	initializer_list<int> li2 =  1, 2, 3, 4 ;
	cout << typeid(li).name() << endl;

	vector<int> v =  1, 2, 3, 4, 5 ;
	list<int> l 10, 20, 30 ;
	//vector<Date> vd =   2001, 1, 1 ,  2002, 2, 2  ;
	map<string, int> dict =  make_pair("sort", 1),  "insert", 2  ;
	return 0;

decltype

将变量的类型声明成指定的类型

int main()

	int i = 10;
	auto p = &i;
	auto pf = strcpy;
	cout << typeid(p).name() << endl;
	cout << typeid(pf).name() << endl;
	decltype(pf) px;
	cout << typeid(px).name() << endl;

	return 0;

 //decltype的一些使用使用场景
template<class T1, class T2>
auto F(T1 t1, T2 t2) -> decltype(t1 * t2)//箭头指向返回值类型

	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
	
	return ret;


int main()

	F(1, 2.2);

	return 0;

右值引用

C++98中提出了引用的概念,引用即别名,引用变量与其引用实体公共同一块内存空间,而引用的底层是通过指针来实现的,因此使用引用,可以提高程序的可读性。
为了提高程序运行效率,C++11中引入了右值引用,右值引用也是别名,但其只能对右值引用。

左值与右值

可以放在=左边的,或者能够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但是也不一定完全正确。
右值eg:字面常量、表达式返回值,函数返回值等

  • 1、普通类型的变量,因为有名字,可以取地址,都认为是左值。
  • 2、 const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是 const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间), C++11认为是左值
  • 3、 如果表达式的运行结果是一个临时变量或者对象,认为是右值。
  • 4、 如果表达式运行结果或单个变量是一个引用则认为是左值

总结:

  1. 不能简单地通过能否放在=左侧右侧或者取地址来判断左值或者右值,要根据表达式结果或变量的性质 判断
  2. 能得到引用的表达式一定能够作为引用,否则就用常引用。
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 << &rr1 << endl;

	rr1 = 20;//没被存起来,因为是临时变量(右值不能取地址)
	cout << &rr1 << endl;

	// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
	//10 = 1;
	//x + y = 1;
	//fmin(x, y) = 1;

	return 0;

引用与右值引用

普通引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值。

C++11中右值引用:只能引用右值,一般情况不能直接引用左值。

右值不能取地址的,但是给右值取别名后,右值会被存储到特定的位置,且可以取到该位置的位置
int main()

 // 10纯右值,本来只是一个符号,没有具体的空间,
 // 右值引用变量r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量
 int&& r1 = 10;
 r1 = 100;
 int a = 10;
 int&& r2 = a; // 编译失败:右值引用不能引用左值
 return 0;
 
// 1、左值引用  -》 左值
// 2、右值引用  -》 右值


// 3、const 左值引用  -》左值 或 右值
// 4、右值引用  -》 std::move(左值) 


int main()

	// 左值引用只能引用左值,不能引用右值。
	int a = 10;
	int& ra1 = a;   // ra为a的别名
	// int& ra2 = 10;   // 编译失败,因为10是右值

	const int& ra2 = 10;
	const int& ra3 = 10 + 20;

	return 0;


右值引用的场景和意义

namespace test

	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;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\\0
	;

	test::string to_string(int value)
	
		bool flag = true;
		if (value < 0)
		
			flag = false;
			value = 0 - value;
		

		test::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;//编译器把str识别成一个右值
	

//
// 左值引用的使用场景
// 1、做参数 -- 可以
// 2、做返回值 -- 有缺陷
void func1(test::string s)


void func2(const test::string& s)


int main()

	test::string s1("hello world");
	// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
	//func1(s1);
	func2(s1);

	// operator+=可以使用传左值引用返回
	s1 += 'A';

	// to_string 不能用左值引用返回,这个就是左值引用短板
	// 如果函数返回对象除了函数作用域就不在了,就不能使用做引用返回,就会存在拷贝
	test::string ret1 = test::to_string(1234);
	test::to_string(1234);

	return 0;

左值引用的短板:当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。eg:test::to_string(int value) 这里只能传值返回,传值返回会导致至少1次拷贝构造(旧编译器是2次)
右值引用和移动语义解决以上问题: 移动构造本质是将参数右值的资源窃取过来占为己有,不需要深拷贝,所以叫移动构造,就是窃取别人资源来构造自己(没有开新空间拷贝数据,效率提高)

// 移动构造 增加一个参数是右值引用的版本
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		
			//转移资源
	 		cout << "string(string&& s) -- 移动构造" << endl;
			this->swap(s);
		

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

			return *this;
		
namespace test

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

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

			return *this;
		

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

	test::string to_string(int value)
	
		bool flag = true;
		if (value < 0)
		
			flag = false;
			value = 0 - value;
		

		test::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;//编译器把str识别成一个右值
		//使编译器优化,两次移动构造合二为一
	2021.11.6-测试

java数组升序排序,统统都会!

C语言:已有a,b两个链表,每个链表中的节点包括学号,成绩。要求把两个链表合并,按学号的升序排列

Python pandas根据日期范围按升序过滤数据

最短无序连续子数组

581-最短无序连续子数组