c++11
- 1.C++11简介
- 2.列表初始化
- 2.1 {}初始化
- 2.2 std::initializer_list
- 3.变量类型推导
- 3.1 auto
- 3.2 decltype
- 3.3 nullptr
- 4.范围for循环
- 5.final与override
- 6.智能指针
- 7.新增加容器---静态数组array、forward_list以及unordered系列
- 8.默认成员函数控制
- 9.右值引用
- 9.1 左值引用和右值引用
1.C++11简介
C++11 是第二个真正意义上的 C++ 标准,也是 C++ 的一次重大升级。C++11 增加了很多现代编程语言的特性,比如自动类型推导、智能指针、lambda 表达式等,这使得 C++ 看起来又酷又潮,一点也不输 Java和 C#。
2.列表初始化
2.1 {}初始化
class Date
{
public:
Date(int y,int m,int d):_y(y),_m(m),_d(d)
{}
~Date()
{}
private:
int _y;
int _m;
int _d;
};
int main()
{
int a{ 100 };
vector<int> v{ 1,1,1 };
int m[3]{ 5,5,5 };
Date d{ 2021,2,5 };
return 0;
}
2.2 std::initializer_list
可以看出这是一个已经实现好的类;
它的作用呢就是可以实现对所有的类型实现{}赋值;
对于自定义类型可以直接重载一个来使用;
template<class T>
class myvector
{
typedef T* myiterator;
public:
myvector(initializer_list<T> i)
{
start = new T[i.size()];
finsh = start + i.size();
end = finsh;
myiterator it = start;
typename initializer_list<T>::iterator t = i.begin();
while (t != i.end())
{
*it = *t;
it++;
t++;
}
}
~myvector()
{}
private:
myiterator start;
myiterator finsh;
myiterator end;
};
3.变量类型推导
3.1 auto
自动推导类型
int main()
{
myvector<int> mv{ 1,2,3,5,5,6 };
auto v={ 1,2,3,4,5,6 };
cout << typeid(v).name() << endl;
//vector<int> v{ 1,2,3,5,5,6 };
//cout << mv << endl;
/*cout << NULL << endl;
nullptr;*/
return 0;
}
3.2 decltype
关键字decltype将变量的类型声明为表达式指定的类型(和auto差不多)
3.3 nullptr
4.范围for循环
底层就是通过迭代器实现,所以只要实现了迭代器的数据结构都可以使用范围for;
5.final与override
在继承和多态讲过,final表示禁止继承,override表示判断子类继承的虚函数有没有重写;
6.智能指针
因为指针在c/c++只太重要了,所以我们在普通的指针基础上实现智能指针,其中包括unique_ptr等等等,在等后期具体写毕竟指针才是c/c++的精髓~~~!
7.新增加容器—静态数组array、forward_list以及unordered系列
8.默认成员函数控制
因为有了右值引用c++出现了一个移动构造,一个移动赋值重载;
注意:
如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任
意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类
型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
如果实现了就调用移动构造,没有实现就调用拷贝构造。
如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动赋值重载函数,对于内
置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造
完全类似)就是一句话你没有实现移动的构造或者赋值重载的情况下也没有写析构,拷贝构造,拷贝赋值重载的情况下编译器才会给你实现一个默认的移动。。。;
如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
强制生成默认函数的关键字default:
C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原
因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以
使用default关键字显示指定移动构造生成
class Person
{
public:
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p)
:_name(p._name)
, _age(p._age)
{}
Person(Person&& p) = default;//有这句s3会调用这个没有的话就调用上面的一个
private:
string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
return 0;
}
禁止生成默认函数的关键字delete:
如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
9.右值引用
9.1 左值引用和右值引用
传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们
之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。
什么是左值?什么是左值引用?
左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
int main()
{
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;
return 0;
}
class A
{
public:
A(const int& b)
:a(b)
{
cout << "构造&" << endl;
}
A(const int&& b)
:a(b)
{
cout << "构造&&" << endl;
}
void operator=(int&& a)
{
cout << "void operator=(int&& a)" << endl;
}
void operator=(int& a)
{
cout << "void operator=(int& a)" << endl;
}
void operator=(const int&& a)
{
cout << "void operator=(const int&& a)" << endl;
}
void operator=(const int& a)
{
cout << "void operator=(const int& a)" << endl;
}
private:
int a;
};
void fun(A&& a)
{
a = 100;
}
int main()
{
const int m = 10;
A b(m);//左值
A a(100);//右值
A c(move(m));//右值
a = m;//左值
b = 100;//右值
c = move(m);//右值
fun(100);
fun(m);
return 0;
}
一般情况我们右值不是这么玩的,而是在模板里面实现一种叫做完美转发的事情;
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<typename T>
void PerfectForward(T&& t)
{
Fun(t);
}
int main()
{
PerfectForward(10); // 右值
int a;
PerfectForward(a); // 左值
PerfectForward(std::move(a)); // 右值
const int b = 8;
PerfectForward(b); // const 左值
PerfectForward(std::move(b)); // const 右值
return 0;
}
为什么全部变成了左值呢???看来模板中的&&只是一种可以接收左值和右值的操作!
那么我们怎么样可以实现完美转发呢?
只需要一个forward函数就可!
这个函数不是一次转发就可以,只要是你使用了万能引用的模板都必须使用forward来转发保证变量的特性不会衰减到左值