C++11常用语法-壹

1.列表初始化

C++ 98之中,只能对数组进行列表初始化,在C++ 11 之中添加了新语法,可以对内置类型列表初始化、自定义类型的列表初始化

1.1内置类型列表初始化

在这里插入图片描述

1.2自定义类型列表初始化

1.支持单个对象的列表初始化
在这里插入图片描述

2.多个对象的列表初始化

多个对象想要初始化,需要给该类添加一个带有initializer_list类型参数的构造函数即可。
initializer_list是系统自定义的类模板,该模板中主要有三个方法:begin(),end()迭代器,以及获取区间中元素个数的方法size()

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

2.变量类型的推导

2.1 编译时类型推导auto

作用:简化类型写法
缺点:可读性变差

auto是编译时,根据初始化表达式类型进行推导的
因此,auto对运行时的类型推导是无能为力的

decltype是根据表达式的实际类型推演出定义变量时所用的类型

所以,auto和decltype不支持作为函数的参数:
1.语法原因,是编译时推导的
2.函数编译成指令,需要先建立起栈桢,那么就需要计算变量的大小,那么就需要提前知道变量的类型

2.2运行时类型推导 typeid

C++ 98支持的RTTI(运行时类型识别)

typeid只能查看类型不能用其结果定义类型,因为一个函数栈桢的建立需要计算其变量的大小,如果是运行时推导,那么就无法计算大小

dynamic_cast只能应用于含有虚函数的继承体系中

运行时类型识别的缺陷是降低程序运行的效率

1.推演表达式类型作为变量的定义类型
2.推演函数返回值的类型
在这里插入图片描述

3.final、override

final:修饰虚函数,表示该虚函数不能再被继承

override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
详情

4.新增容器

静态数组 array:没什么用,在栈上开辟的静态数组,灵活性不够
在这里插入图片描述
forward_list:单链表
unordered系列

5.默认成员函数控制

C++中的空类,会默认生成一些成员函数,但是这些函数如果程序员自己编写了,就不会默认生成。然而有时候又需要默认生成,这就容易造成混乱,因此C++11,提供两个关键字,让程序员自己决定是否需要编译器生成

default
在默认函数定义或者声明时加上=default,可以显示的指示编译器生成该函数的默认版本,用=default修饰的函数称为显示缺省函数

delete
在C++98之中,将函数设置成private并且不给定义,其它人就无法调用
在C++11之中,主需要在函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,常用于防止拷贝
在这里插入图片描述

6.右值引用

左值:使用空间
右值:使用内容

C++ 11 中对右值进行了严格的区分(除了右值就是左值):
纯右值(比如常量): 比如 a+b, 100

将亡值(比如临时变量): 比如表达式的中间结果,函数按照值的方式进行返回

左值引用一般是给左值取别名(const 给右值取别名,不可修改),右值引用一般是给右值取别名(move给左值取别名)

6.1普通引用和右值引用

C++98之中:普通引用只能引用左值,不能引用右值,const引用既可以引用左值,又可以引用右值

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

6.2 移动语义

C++ 11 提出了移动语义的概念:将一个对象中资源转移到另一个对象的方式,可以有效的缓解拷贝构造对象时,资源浪费的情况

6.3为什么提出右值引用

右值引用表示指向的实体为右值,实体的资源可以直接被拿走,提高拷贝的效率,本质上为浅拷贝

#include "test.h"

class String
{
public:

	String(const char *str="")
		:_str(new char[strlen(str) + 1])
	{
		strcpy(_str, str);
		cout << "String(const char *str="")" << endl;
	}

	//正常构造
	String(const String &str)
		:_str(new char[strlen(str._str)+1])
	{
		strcpy(_str, str._str);
		cout << "String(const String &str)" << endl;
	}

	//移动构造
	String(String &&str)//是右值
		:_str(str._str)
	{
		str._str = nullptr;//直接进行资源转移,空间的交换
		cout << "String(const String &&str)" << endl;
	}

	//移动赋值
	String &operator=(String &&str)
	{
		cout << " String &operator=(String &&str)" << endl;
		if (_str != str._str)
		{
			_str = str._str;
			str._str = nullptr;
		}
		return *this;
	}

	//正常赋值
	String& operator=(const String &str)
	{
		if (_str != str._str)
		{
			cout << "String &operator=(String &str)" << endl;

			_str = new char[strlen(str._str) + 1];

			strcpy(_str, str._str);
		}

			return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[]_str;
			cout << "~String()" << endl;
		}
	}

private:
	char *_str;
};

String Getstring()
{
	String ret("123");
	return ret;
}

void test()
{
	cout << "使用右值,移动构造" << endl;
	String str = Getstring();
	cout << endl;
	
	cout << "使用左值,正常构造"  << endl;
	String str2(str);
	cout << endl;

	cout << "使用左值,正常赋值"  << endl;
	str = str2;
	cout << endl;

	cout << "使用右值,移动赋值" << endl;
	str = "123";
	cout << endl;

}

int main()
{
	test();

	system("pause");
	return 0;
}

在这里插入图片描述

在这里插入图片描述

6.4总结

在这里插入图片描述
左值引用的作用:
1.函数引用传参:减少拷贝 + 输出型参数
2.函数引用返回值:减少拷贝
3.提高效率 + 提高程序可读性(不需要复杂指针)

左值引用解决问题的盲区:有些函数不能引用返回(返回对象的空间被销毁了,因此需要生成临时变量,发生深拷贝)
右值引用可以解决这个盲区:借助移动构造和移动赋值,区分出传值返回时,返回的是右值(将亡值)。这样可以直接进行浅拷贝,减少了深拷贝,提高了效率(右值引用,主要是为自定义类型设定的,减少空间的开辟)

C++类型匹配的时候,优先走最匹配的,没有最匹配的,那么走转换一下可以匹配的。也就是说同一个接口函数(重载),通过左值引用和右值引用区分出左值和右值,有如下几点特点:
在这里插入图片描述

6.5右值引用引用左值

6.5.1使用方法介绍

在某些场景之下,需要用右值去引用去引用左值实现移动语义。当需要一个右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数位于头文件之中,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义
在这里插入图片描述

6.5.2使用场景分析

自定义类型String和上述一致

场景1:

class Person
{
public:

	Person(const String &name)
	:_name(name)//调用String的拷贝构造函数
	{}

	Person(const Person& pl)
		:_name(pl._name)
	{
		cout << "Person(const Person& pl)" << endl;
	}

private:
	String _name;
};

在这里插入图片描述

场景2:对场景1的拷贝构造进行优化,使用右值引用:

class Person
{
public:

	Person(const String &name)
	:_name(name)//调用String的拷贝构造函数
	{}

	Person(const Person& pl)
		:_name(pl._name)
	{
		cout << "Person(const Person& pl)" << endl;
	}

	Person(const Person&& pl)
		:_name(pl._name)//pl中的string是左值,所以还是调用的深拷贝	
	{
		cout << "Person(const Person&& pl)" << endl;
	}

private:
	String _name;
};

在这里插入图片描述

场景3:使用进一步的优化

class Person
{
public:

	Person(const String &name)
	:_name(name)//调用String的拷贝构造函数
	{}

	Person(const Person& pl)
		:_name(pl._name)
	{
		cout << "Person(const Person& pl)" << endl;
	}

	Person(Person&& pl)
		:_name(move(pl._name))//pl既然是一个将亡值,那么它的资源也是一个将亡值
	{
		cout << "Person(Person&& pl)" << endl;
	}


private:
	String _name;
};

在这里插入图片描述

移动语义错误用例:
在这里插入图片描述

移动语义正确使用场景:需要保证属性被修改的左值后面不会再用到

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值