1.{}的初始化设置
1.1提出问题
当我们对于内置类型的初始化或者容器初始化,我们通常是需要自己先定义类型,然后for循环插入数据,这样写出来的代码,不够简洁,
C++11 扩大了用大括号括起的列表 ( 初始化列表 ) 的使用范围,使其可用于所有的内置类型和用户自定 义的类型,使用初始化列表时,可添加等号 (=) ,也可不添加 。如下面的例子
#include<iostream>
#include<vector>
#include<map>
using namespace std;
int main()
{
// 内置类型变量
int x1 = { 10 };
int x2{ 10 };
int x3 = 1 + 2;
int x4 = { 1 + 2 };
int x5{ 1 + 2 };
// 数组
int arr1[5]{ 1,2,3,4,5 };
int arr2[]{ 1,2,3,4,5 };
// 动态数组,在C++98中不支持
int* arr3 = new int[5]{ 1,2,3,4,5 };
// 标准容器
vector<int> v{ 1,2,3,4,5 };
map<string, int>dict1 = { pair<string,int>("sort",1),pair<string,int>("insert",2) };
map<string, int>dict2 = { {"sort",1},{"insert",2} };
map<int, int> m{ {1,1}, {2,2,},{3,3},{4,4} };
return 0;
}
1.2 总结初始化列表
① 自定义支持->重写构造函数,一般是直接有的可以直接 调用
② 容器->C++11引入initializer_list,初始化列表,底层逻辑是迭代器加push_back支持。
#include <initializer_list>
template<class T>
class Vector {
public:
// ...
Vector(initializer_list<T> l) : _capacity(l.size()), _size(0)
{
_array = new T[_capacity];
for (auto e : l)
_array[_size++] = e;//这里跟迭代器加尾插的思想一样
}
Vector<T>& operator=(initializer_list<T> l) {
delete[] _array;
size_t i = 0;
for (auto e : l)
_array[i++] = e;
return *this;
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
2 变量类型推导auto与范围for
早期的auto只是用来表示变量的自动化创建和销毁,但是在C++11中用来表示类型推导,这个提出来的目的主要是解决,迭代器类型过长的问题。
#include<iostream>
#include<map>
int main(){
std::map<std::string, std::string> m{ {"apple", "苹果"}, {"banana","香蕉"} };
// 使用迭代器遍历容器, 迭代器类型太繁琐
//std::map<std::string, std::string>::iterator it = m.begin();
auto it = m.begin();
while (it != m.end())
{
std::cout << it->first << " " << it->second << std::endl;
++it;
}
return 0;
}
但是,auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型。我们注意一个问题,当在表达式或者返回值,需要编译运行,才能知道的类型,auto就显得无能为力,这个时候就需要使用decltype
void* GetMemory(size_t size)
{
return malloc(size);
}
int main()
{
int a = 10;
int b = 20;
// 用decltype推演a+b的实际类型,作为定义c的类型
decltype(a + b) c;
cout << typeid(c).name() << endl;
// 如果没有带参数,推导函数的类型
cout << typeid(decltype(GetMemory)).name() << endl;
// 如果带参数列表,推导的是函数返回值的类型,注意:此处只是推演,不会执行函数
cout << typeid(decltype(GetMemory(0))).name() << endl;
return 0;
}
3. nullptr 与智能指针
在C++11中,为了区别0和空指针,提出了nullptr,出于安全考虑
4.STL中的变化
①增加新容器
②针对旧容器,基本增加了移动构造,移动赋值,所有的插入接口函数,都增加了右值引用的版本,用来提高效率。
5.左值和右值
左值:可以获取地址+可以赋值。const修饰的左值只能取地址。
左值引用:给左值区别,只能引用左值,不能引用右值,但是const左值引用可以引用左值也可以引用右值。
主要应用场景:void push_back(const T&x),这里传参实参既可以是左值也可以是右值。
右值: 不能出现在赋值符号的左边,也就是不能修改,也不能取地址。只能出现在右边
int main()
{
//左值
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;
//常见右值
10;
double x = 1.1, double y = 2.2;
x + y;
fmin(x + y);
//右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
return 0;
}
右值引用:右值不能取地址,但是右值引用会将右值存到特定的位置,取到该地址的位置。也可以修改。加个const就不能修改右值引用。
只能引用右值,不能引用左值。非要用呢?移动语义 int&& rr1=std::move(a);
5.1使用场景