一 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 };
九 引入正则表达式
十 引入线程库
线程等待,分离,条件变量,互斥量等
下面推荐一篇个人觉得挺好的文章