> 文章列表 > 【C++】C++11新特性——基础特性

【C++】C++11新特性——基础特性

【C++】C++11新特性——基础特性

文章目录

  • 一、列表初始化
    • 1.1 {}初始化
    • 1.2 initializer_list类型
  • 二、类型推导
    • 2.1 auto
    • 2.2 auto注意事项
    • 2.3 decltype
  • 三、新增与改进
    • 3.1 nullptr
    • 3.2 范围for
    • 3.3 array
    • 3.4 forward_list
    • 3.5 unordered系列
    • 3.6 final与override

一、列表初始化

1.1 {}初始化

C++11 引入了一个新的初始化方式, 称为初始化列表(List Initialize), 具体的初始化方式如下:
初始化列表可以用于初始化结构体类型, 例如:

struct Test
{int _a;double _b;
};int main()
{Test tOld = { 1, 1.1 };Test tNew{ 2, 2.2 };// 列表初始化int a = 1;int b = { 1 };// 列表初始化int c{ 1 };// 列表初始化int arr1[] = { 1,2,3 };int arr2[]{ 1,2,3 };// 列表初始化return 0;
}

以前{}只能用来初始化结构体和数组,而现在一切皆可用列表初始化。

当然new操作符也可以使用{}初始化:

int* ptr1 = new int[] {1, 2, 3};
Test* ptr2 = new Test[]{ {1, 1.1}, {2, 2.2} };

对于自定义类型{}会调用它的构造函数初始化:

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;std::cout << "Date(int year, int month, int day)" << std::endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1, 2, 3);Date d2{1, 2, 3};return 0;
}

【C++】C++11新特性——基础特性
其他一些不方便初始化的地方使用, 比如stl的初始化, 如果不使用这种方式, 只能用构造函数来初始化, 难以达到效果 。

int main()
{std::vector<int> v={ 1, 2, 3, 4, 5 };// 列表初始化std::list<int> lt{ 1, 2, 3, 4, 5, 6 };// 列表初始化for (auto& e : v){std::cout << e << " ";}std::cout << "\\n";for (auto& e : lt){std::cout << e << " ";}std::cout << "\\n";return 0;
}

【C++】C++11新特性——基础特性
这里明显不是使用构造函数初始化的
那么这里是怎么做到的呢?
利用std::initializer_list类型。

1.2 initializer_list类型

int main()
{auto tmp = { 1, 2, 3, 4 };std::cout << typeid(tmp).name() << std::endl;return 0;
}

【C++】C++11新特性——基础特性typeid(变量名).name() 查看变量的类型
我们可以把它理解为一个存在常量区数组
这个容器有以下接口:
【C++】C++11新特性——基础特性
这样我们就可以像迭代器一样使用这个容器。
所以vector和list就可以这样初始化:
【C++】C++11新特性——基础特性
【C++】C++11新特性——基础特性
其实使用initializer_list初始化就是遍历initializer_list的内容然后一个一个插入。

vector(initializer_list<T> il):_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{reserve(il.size());// 防止过度扩容typename initializer_list<T>::iterator it = il.begin();while (it != il.end()){push_back(*it);++it;}
}

二、类型推导

2.1 auto

auto的作用就是自动推导对象的类型。从这个意义上讲,auto并非一种"类型"声明,而是一个类型声明时的"占位符",编译器在编译时期会将auto替换为变量实际的类型。

double foo()
{return 1.1;
}struct Test
{};int main()
{// 内置类型int a = 1;auto b = a;// intauto c = foo();// double// 自定义类型Test test;auto ret = test;// struct Testreturn 0;
}

2.2 auto注意事项

1️⃣ 首先定义变量的时候一定要初始化

auto a;// error
a = 10;

2️⃣ 不能作为函数的形参

void func(auto a) {}// error

3️⃣ 变量不能作为自定义类型的成员变量

struct Test
{auto a = 1;// error
};

4️⃣ 不能是auto数组

auto arr[] = { 1, 2, 3 };// error

5️⃣ 模板实例化类型不能是auto类型

std::vector<auto> v;// error

2.3 decltype

关键字decltype将变量的类型声明为表达式指定的类型。我前面用的typeid(x).name()可以拿到x类型的字符串,但是不能使用这个再去定义一个变量。
而decltype却可以拿到变量,后面还可以继续使用这个类型定义出变量。

int main()
{int x = 0;decltype(x) a = 10;// intdouble y = 0.0;decltype(x + y) b;// doublereturn 0;
}

三、新增与改进

3.1 nullptr

nullptr 是为了解决原来 C++中 NULL 的二义性问题而引进的一种新的类型,因为NULL既能表示整型也能表示指针。

#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else  
#define NULL    ((void *)0)
#endif  
#endif  /* NULL */

3.2 范围for

在 C++中 for 循环可以使用基于范围的 for 循环,示例代码如下:

int main()
{int a[] = { 1, 2, 3, 4, 5, 6 };for (int& e : a){std::cout << e << " ";}std::cout << '\\n';return 0;
}

3.3 array

template < class T, size_t N > class array

array就是个数组,它跟vector的区别是array是静态数组,不能扩容。
那么它跟普通数组有什么区别呢?

普通的数组对越界的检查是随机的,如果越界可能不会报错,但是array一定能检查出来

int main()
{int a1[10];a1[22];// 不报错std::array<int, 10> a2;a2[22];// 报错return 0;
}

3.4 forward_list

template < class T, class Alloc = allocator<T> > class forward_list

forward_list就是一个单链表,它只有头插和头删接口,并没有尾插和尾删接口。

int main()
{std::vector<int> a = { 1, 2, 3, 4, 5, 6 };std::forward_list<int> lt(a.begin(), a.end());for (auto& e : lt){std::cout << e << " ";}std::cout << '\\n';return 0;
}

3.5 unordered系列

这个在之前的文章有详细介绍:
【C++】哈希

跟map与set相比效率得到了大提升

3.6 final与override

这里在多态里面有过详细介绍:
【C++】多态

final关键字在父类修饰虚函数,表示该虚函数不能再被重写。
override在子类修饰虚函数,检查子类是否重写,如果没有重写则编译报错。



ZAN8艺术网