C++11新特性

C++11引入了许多新特性,如auto/decltype自动推导类型,基于范围的for循环简化遍历,final、override和delete关键字的使用,以及右值引用和移动语义。还新增了unordered_map和unordered_set,智能指针如shared_ptr和uniq_ptr,统一初始化语法,正则表达式支持以及线程库等,大大提高了编程效率和代码质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 auto/decltype:自动推导类型

之前的C++标准库里面,必须显示指定变量的类型,然而随着模板类型的出现,某些东西的类型,尤其是函数参数的返回值不是那么容易表示。在这种情况下,将中间结果存储在某个变量中是件很困难的事,要去了解模板的内部结构。

c++11提供以下方式两种来缓解上述问题

1.定义有显示初始化变量可以用auto来自动确定变量类型

2.decltype可以用来在编译期间确定表达式的类型

auto:用法示例:

#include<vector>
int main()
{
	int a = 10;
	auto b = a;
	//使用场景
	//之前的写法
	vector<int> v1 = { 1, 2, 3, 4 };
	vector<int>::iterator it = v1.begin();
	for (it = v1.begin(); it != v1.end(); ++it)
	{
		if (*it == 2)
		{
			cout << "存在"<<endl;
		}
	}
	//有了auto之后的写法
	auto it1 = v1.begin();
	for (it1 = v1.begin(); it1 != v1.end(); ++it1)
	{
		if (*it1 == 2)
		{
			cout << "存在"<<endl;
		}
	}
	system("pause");
	return 0;
}

decltype用法示例:

    int a;
	decltype (a)b = 5;
	cout << b;

二  基于范围的for循环

在以前的C++版本中,要遍历一个容器的元素需要很多代码,而c++11推出基于范围的for循环,它会遍历列表中的每一个元素。

可用于数组,能用迭代器遍历的容器。(通常和auto搭配使用)

int myarray[5] = { 1, 2, 3, 4, 5 };
	for (int& x : myarray)
	{
		x *= 2;
	}
	for (int x : myarray)
	{
		cout << x;
	}
	vector<int> v1 = { 6, 2, 3, 4, 5 };
	vector<int> ::iterator it = v1.begin();
	for (auto& it : v1)//修改就用&  it此时就相当于容器中第一个元素,然后依次向后走,直到遍历完容器中所有元素
	{
		it = it * 2;
	}
	for (auto it : v1)//遍历不用&
	{
		cout << it << endl;
	}
	cout << endl;

三  引入final,override,delete,default关键字

default:显示缺省函数

功能:在默认函数定义或声明的时候加上=default,从而显示指定编译器生成该函数的默认版本。

default:我们知道类有六个默认成员函数,如果不写,编译器会自动生成,但是如果写了,编译器便不会再自动生成。

class String
{
public:
	String(const char* str)
	{}

};
int main()
{
	String s("haha");
	String s;//编译不通过,没有默认的构造函数可用
	system("pause");
	return 0;
}

default的作用就是告诉编译器,虽然我写了,但是你还是要给我生成默认的构造函数

class String
{
public:
	String(const char* str)
	{}
	String()= default;
};
int main()
{
	String s("haha");
	String s1;//编译通过,因为生成了默认的构造函数
	system("pause");
	return 0;
}

delete:删除函数(用于防拷贝)

作用:在函数定义或声明时加上=delete,阻止任何函数调用。

使用场景:之前我们知道如果我类实现防拷贝,就是把拷贝构造和赋值运算符的重载只声明不实现,并且给为私有的,

但是这种有一个缺陷就是,如果我要访问的这个函数,是你这个类的友缘。那就可以了。不能起到防拷贝的目的

那这时你就要把这个函数给成delete函数,

class String
{
public:
	String(const char* str) = delete;
	
	String()= default;
};
int main()
{
	String s("haha");
	String s1;//编译通过,因为生成了默认的构造函数
	system("pause");
	return 0;
}

final:

修饰虚函数,放虚函数后面虚函数不能被重写

修饰类时表示该类不能被继承。

override:

写在虚函数后面,保证了虚函数一定要重写,如果不重写,就报错。

四 右值引用

左值:一般来说就是能放在=左边的值,可被修改,有名字,可以取地址。

右值:放在=右边的值不一定是右值,不能被修改的值为右值,不能取地址。

右值引用:就是对一个右值进行引用的类型,右值通常不具有名字,只能通过引用的方式找到它的存在。即匿名变量的别名

左值引用:c++98的引用,具有名字变量的别名。

左值引用可以引用右值(加上const),也可以引用左值

右值引用只能引用右值。

int a = 5;
	int&& b = 5;//右值引用,引用右值(可以)
	int&& b = a;//右值引用,引用左值(不可以)
	const int& b = 5;//左值引用,引用右值(可以,加const)
	int& b = a;//左值引用,引用左值(可以)

std::move()可以将值强制转换为右值,用右值引用进行引用。move转换的左值变量声明周期并没有随着左右值的转换变化。

move更多作用在声明周期即将结束的对象上

五:移动语义

C++中,如果没有显示提供类的构造函数,拷贝构造,赋值运算符的重载,析构函数,编译器默认合成一个浅拷贝的,

如果没有涉及到资源的管理,浅拷贝没有问题,但是涉及到资源的管理,就要用户自己提供,构造拷贝构造(等深拷贝)

但是深拷贝有时代价太大,比如返回用一个临时对象,去拷贝构造另一个对象,拷贝这个对象要开空间,完之后临时对象被释放,那不如把临时对象的资源转给这个要构造的对象。岂不美哉。这就是移动语义。

作用:资源的转移,移动语义也就是移为己用。

移动语义是如何被触发的?遇到临时对象。把临时对象的资源全部给另一个对象。

class String
{
public:
	String()
	{}
	String(const char*str)
		:_str(new char[strlen(str)+1])
	{
		strcpy(_str, str);
	}
	String(const String& s)
		:_str(NULL)
	{
		String tmp(s._str);
		swap(_str, tmp._str);
	}
	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
	//移动构造
	String(String&& s)//资源转移
	{
		cout << "String(String&& s)" << endl;
		_str = s._str;
		s._str = NULL;
	}
	//移动赋值
	String& operator=(String&& s)
	{
		cout << "String& operator=(String&& s)" << endl;
		swap(_str, s._str);
		return *this;
	}
	String operator+(const String& s)
	{
		String ret;
		ret._str = new char[strlen(_str) + strlen(s._str) + 1];
		strcpy(ret._str, _str);
		strcpy(ret._str + strlen(_str), s._str);
		return ret;//要拷贝临时对象返回,ret也不用了,那我们就把ret销毁。而临时对象构造s3后也销毁,而且又要构造s3,这样没有意义,影响效率
		           //String s3=s1+s2;那能否在构造s3的时候不分配空间,而是把刚才临时对象的资源空间吸取过来,这就是移动语义
	}
	~String()
	{
		delete[]_str;
		_str = NULL;
	}
	char* c_str()
	{
		return _str;
	}
private:
	char* _str;
};
int main()
{
	String s1("hello");
	String s2("world");
	String s3 = s1 + s2;//此处就用的移动构造    一般处理将亡值
	cout << s3.c_str() << endl;
	system("pause");
	return 0;
}

六  unordered_map 和unordered_set

1.我们知道map和set底层是红黑树,而unordered_map和unordered_set底层是哈希桶

2.map和set走中序有序(因为底层搜索树),而unordered_map和unordered_set走中序无序

3.map的key要求,不能冗余。value有缺省的构造函数。而unordered_map要求key可哈希就行。

4.map和set的增删查改都为lgn-2lgn,而unordered_map为0(1)

七  智能指针

C++11中废弃了98中的auto_ptr,引入了shared_ptr和uniq_ptr

uniq_ptr 防拷贝   把拷贝构造函数和赋值运算符重载给为私有的,并且只声明不实现

shared_ptr引用计数,有循环引用的问题。

八 统一的初始化语法

以前在c++98中,标准允许使用{}对数组元素进行统一的列表初始值设定,但对于自定义类型确不可以

c++11使用了{}统一初始化的方式。

//数组
	int array[] = { 1, 2, 3, 4, 5 };
	int array[5] = { 0 };
	//但对于自定义类型,确无法使用这样的初始化
	vector<int> v1{ 1, 2, 3, 4 };
	map<int, int>m{ { 1, 1 }, { 2, 2 }, { 3, 3 } };
	//定义变量 
	int a = 10;
	int a{ 10 };

九  引入正则表达式

十  引入线程库

线程等待,分离,条件变量,互斥量等

 

下面推荐一篇个人觉得挺好的文章

https://harttle.land/2015/10/08/cpp11.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值