中级C++11:大括号初始化、decltype、typeid、右值引用、移动语义、完美转发

C++11特性

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

大括号的花样使用

  • C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
  • 调用的自定义类型的构造函数。
int main()
{
	int a[]{ 21, 22, 23, 24 };
	map<string, int> v1{ {"endeavour",24} };//主要支持了initializer_list 作为参数的构造函数

	//vector<int> v2(1, 2, 3); 必须要有对应参数类型和个数的构造函数

	initializer_list<int> lt1{ 21,22,23 };
	auto it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	vector<int>* v2 = new vector<int>[4]{ {24,25,26} };
	int* p1 = new int[3] { 24, 25, 26 };
	int* p2 = new int(27);
	return 0;
}

变量类型的推导decltype、typeid

  • 推演表达式类型作为变量的定义类型
  • 推演函数返回值的类型
int main()
{
	int a[]{ 21, 22, 23, 24 };
	map<string, int> v1{ {"endeavour",24} };
	initializer_list<int> lt1{ 21,22,23 };
	
	auto pa = &a;
	auto pm = &v1;
	auto plt = &lt1;

	vector<decltype(pa)> vv;//返回的是一个类型

	cout << typeid(pa).name() << endl<<endl;//拿到的是类型的字符串
	//cout << typeid(pm).name() << endl<<endl;
	cout << typeid(plt).name() << endl<<endl;
	cout << typeid(vv).name() << endl << endl;
	return 0;
}

增加的新容器

在这里插入图片描述

int main()
{
	int a[]{ 21, 22, 23, 24 };
	array<int, 5>  ai{ 25,26,27 };
	a[11] = 28;//对越界是一种抽查
	ai[6] = 29;//检查更严格。

	//另外有用的就是 哈希表的 map set
	return 0;
}

右值引用

  • 普通左值引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值。
  • 右值引用:只能引用右值,一般情况不能直接引用左值。
    • std::move()函数位于 头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。
    • 被转化的左值,其生命周期并没有随着左值的转化而改变,即std::move转化的左值变量lvalue不会被销毁。
    • STL中也有另一个move函数,就是将一个范围中的元素搬移到另一个位置。
      在这里插入图片描述
      在这里插入图片描述
int main()
{
	int a = 24;//左值可以取地址,可以赋值;const左值可以取地址,不能赋值,在内存中有地址。
	int& b = a;
	int c = b;
	const int& d = 24;  //const左值可以引用右值

	
	//25 = a;            //右值具有常性,不能取地址,不能出现再取值符号的左边,不能修改,因为在内存中没有地址
	//25 = 1;
	//int* pc = &25;

	int&& f = 25;  //右值引用后的c  是左值。
	f = 27;

	//int&& e = a;  //右值引用不能引用 左值
	int&& e = move(a);  //右值引用可以引用 move以后的左值

	return 0;
}

在这里插入图片描述

左值引用的盲区:

mystring::String f2()
{
	mystring::String s("hhee");
	return s;
}
int main()
{
	mystring::String s1 = f2();
	return 0;
}
  • s返回前 会产生一个临时对象,深拷贝拷贝s;tmp(s);然后s出作用域自动析构;
  • 然后s1(tmp),在进行一次深拷贝构造 ,这个过程连续,被编译器优化了
  • 这里的 = 号 不是赋值重载,这里是刚创建的对象用一个对象初始化。
  • 如果s的数据量很大,那么开销很大,两次申请,两次strcpy,两次析构…
  • 如果加static,引用返回,会有多线程安全问题:多线程使用的都是这一份。
    在这里插入图片描述

移动语义

  • 将一个对象中资源移动到另一个对象中的方式,可以有效缓解该问题。
  • 有了移动语义后,函数外若有对象接受返回的参数时,会把返回值处理成 将亡值(右值)。直接进行一次移动构造即可,不用在进行原来的两次拷贝构造。
    在这里插入图片描述
    在这里插入图片描述
namespace mystring
{
    class stirng
   {
   ....
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
   };
}

swap

      // s1.swap(s2)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

移动构造

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

移动赋值

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

			return *this;
		}

在这里插入图片描述


右值引用、移动语义总结:

  • 所有的STL容器都增加了移动构造,移动赋值重载;面对常量时,效率更高。
    在这里插入图片描述

  • C++11中,算法里面的swap和容器里面的swap效率没有区别。
    在这里插入图片描述

右值引用

  1. 实现移动语义(移动构造与移动赋值)
  2. 给中间临时变量取别名:
  3. 实现完美转发…
int main()
{
 string s1("hello");
 string s2(" world");
 string s3 = s1 + s2; // s3是用s1和s2拼接完成之后的结果拷贝构造的新对象
 stirng&& s4 = s1 + s2; // s4就是s1和s2拼接完成之后结果的别名  表达式结果具有常性时右值
 return 0;
}

完美转发

  • 完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。
  • 如果相应实参是左值,它就应该被转发为左值;如果相应实参是右值,它就应该被转发为右值。
  • 模板里面的T&& 做参数,不再局限是右值引用,叫做万能引用;即可引用左值,也可以引用右值;但接受一个参数后民退化成左值,用std::forward<T>可以保持右值属性。
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<class T>
void PerfectForward(T&& x)
{
	//Fun(x);
	Fun(std::forward<T>(x));
}


新增的成员函数

移动构造/移动赋值

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

  • 如果 没有定义 拷贝构造/拷贝赋值/移动构造/移动赋值/析构函数的 任何一个,编译器会 自动生成 移动构造/移动赋值 函数
  • 如果 需要定义 拷贝构造/拷贝赋值/移动构造/移动赋值/析构 函数的任何一个,不要忘了 移动构造/移动赋值 函数,否则右值会走普通的拷贝构造。
  • 条件很苛刻,对于需要深拷贝的类,都需要自己写。

G.

  • 大脑一片混乱…
    请添加图片描述
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值