C++11新特性总结学习

1 nullptr

主要用来弥补NULL的不足,在早期的C++中,将数字0和NULL视为等效,进而在特殊情况出现混乱,特别是在重载中容易混淆。

nullptr 的类型为 nullptr_t,能够隐式的转换为任何指针或成员指针的类型。

//如果NULL又被定义为0, 那么fun(NULL)将会调用函数fun(int),这与预期不相符合。
class Example{
    void fun(int);
    void fun(char *)
}

2 auto

auto在C++早起版本中作为存储类型指示符,与register并存,如果一个变量没有申明为register,将自动视为auto变量。后续版本中register逐渐被弃用,因此auto的作用被重新定义。

auto作类型推导,最常见的例子是迭代器。

//没有auto之前,写迭代器的方式
for(vector<int>::const_iterator itr = vex.begin(); itr != vec.end(); ++itr) {}
//使用auto之后,可以变为
//因为begin()返回vector<int>::const_iterator类型,所以itr也是同类型
for(auto itr = vec.begin(); itr != vec.end(); ++itr) {}

特别要注意的是:

  • auto只能对变量进行类型推导。
  • auto不能用于函数参数。
  • auto不能用于推导数组类型。
//错误示例
int add(auto a, auto b);

3 decltype

decltype解决了auto只能对变量进行类型推导的缺陷。decltype的用法:

decltype(表达式)

如果需要计算某个表达式的类型,可以利用decltype

//decltype计算表达式类型
auto a = 1; auto b = 2;
decltype(a + b) c;    //c的类型为(a + b)和的类型

拖尾返回类型

C++11引入了拖尾返回类型

template<typename T, typename U>
//首先可能想到使用下面代码,但在编译器读到decltype(x+y)时,x和y尚未被定义。 
decltype(x+y) add(T x, U y);    //无法通过编译
//C++11引入了拖尾返回类型,解决了上述问题。
auto add(T x, U y) -> decltype(x + y) {
    return x + y;
}

C++14 开始可以直接让普通函数具备返回值推导,因此下面的写法变得合法

template<typename T, typename U>
auto add(T x, U y) {
    return x+y;
}

4 基于范围的for循环

引入了for基于范围的迭代写法,使得代码变得简单。

//以前对vector遍历的写法
std::vector<int> arr(5, 100);
for(std::vector<int>::iterator i = arr.begin(); i != arr.end(); ++i) {
    std::cout << *i << std::endl;
}

//引入for新特性之后的遍历,其中&用了引用
for(auto &i : arr) {    
    std::cout << i << std::endl;
}

5 模板增强

外部模板

在传统C++中模板在使用中会被实例化,也就是说只要在每个编译单元(文件)中遇到被完整定义的模板,都会被实例化。这就可能会出现重复实例化的情况,进而导致编译的时间增加。

C++11引入了外部模板,改变了原来强制编译器在特定位置实例化模板的语法,从而可以显示的告诉编译器何时进行模板的实例化,减少了编译时间。

//传统模板,在每个编译文件中均会强行实例化
template class std::vector<bool>;      
//外部模板,不在该编译文件中实例化模板
extern template class std::vector<double>;  

尖括号

C++11中对连续的>>有了新的合法定义。进而使得下述代码变得合理。

std::vector<std::vector<int>> various;

类型别名模板

传统 C++中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。

template<typename T, typename U, int value>
class SuckType {
public:
    T a;
    U b;
    SuckType():a(value),b(value){}
};
template< typename U>
//传统C++中,改写法不合法
typedef SuckType<std::vector<int>, U, 1> NewType; // 不合法
//C++11以后,利用下述写法
using NewType = SuckType<int, U, 1>;    // 合法

默认模板参数

默认模板参数使得,在使用模板函数时,当不指定模板参数类型时,函数会按照默认类型处理。

//未指定参数类型
template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y) {
    return x+y
}
//指定默认参数类型
template<typename T = int, typename U = int>
auto add(T x, U y) -> decltype(x+y) {
    return x+y;
}

6 新增容器

std::array

std::array保存在栈内存中,相比堆内存中的std::vector,访问更加灵活,具有更高的处理性能和效率。

std::array会在编译时创建一个固定大小的数组,不能够被隐式的转换成指针,使用时只需指定其类型和大小即可:

//std::array<数组类型, 数组大小>
std::array<int, 4> arr= {1,2,3,4};
//数组大小必须固定,且必须时常量
int len = 4;
std::array<int, len> arr = {1,2,3,4}; //非法, 数组大小参数必须是常量表达式

使用 std::array时,遇到要将其兼容C风格的接口时,这里有三种做法:

void foo(int *p, int len) {
    return;
}
std::array<int 4> arr = {1,2,3,4};

//C风格接口传参
//foo(arr, arr.size());         //非法, 无法隐式转换为指针
foo(&arr[0], arr.size());        //合法
foo(arr.data(), arr.size());     //合法

// 使用std::sort
std::sort(arr.begin(), arr.end());

std::forward_list

  • std::forward_list 是一个列表容器,使用方法和 std::list 类似。
  • std::forward_list 是单向链表,std::list 是双向链表。
  • std::forward_list 是标准库容器中唯一不提供 size() 方法的容器。
  • std::forward_list 使用单向链表进行实现,提供了 O(1) 复杂度的元素插入,当不需要双向迭代时,具有比 std::list 更高的空间利用率。

无序容器

C++11 引入了两组无序容器,无序容器中的元素不进行排序,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 O(constant)

  • std::unordered_map / std::unordered_multimap
  • std::unordered_set / std::unordered_multiset

元组 std::tuple

  • std::make_tuple:  构造元组
  • std::get:  获得元组某个位置的值
  • std::tie:  元组拆包
  • 合并两个元组,可以通过 std::tuple_cat 来实现。
auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
#include <tuple>
#include <iostream>

auto get_student(int id) {
    // 返回类型被推断为 std::tuple<double, char, std::string>
    if (id == 0)
        return std::make_tuple(3.8, 'A', "张三");
    if (id == 1)
        return std::make_tuple(2.9, 'C', "李四");
    if (id == 2)
        return std::make_tuple(1.7, 'D', "王五");
    return std::make_tuple(0.0, 'D', "null");   
    // 如果只写 0 会出现推断错误, 编译失败
}

int main() {
    auto student = get_student(0);
    std::cout << "ID: 0, "
    << "GPA: " << std::get<0>(student) << ", "
    << "成绩: " << std::get<1>(student) << ", "
    << "姓名: " << std::get<2>(student) << '\n';

    double gpa;
    char grade;
    std::string name;

    // 元组进行拆包
    std::tie(gpa, grade, name) = get_student(1);
    std::cout << "ID: 1, "
    << "GPA: " << gpa << ", "
    << "成绩: " << grade << ", "
    << "姓名: " << name << '\n';
}

7 正则表达式

正则表达式描述了一种字符串匹配的模式。通过正则表达式可以快速地在诸多字符串中匹配出符合要求的字符串。

C++11 提供正则表达式库操作 std::string 对象,对模式 std::regex (本质是 std::basic_regex)进行初始化,通过 std::regex_match 进行匹配,从而产生 std::smatch (本质是 std::match_results 对象)。

例子:[a-z]+.txt

  • 上述的正则表达式中, [a-z] 表示匹配一个小写字母,
  • + 可以使前面的表达式匹配多次,因此 [a-z]+ 能够匹配一个及以上小写字母组成的字符串。
  •  . 表示匹配任意字符,而. 转义后(\.)则表示匹配字符 . ,txt 表示严格匹配 txt 这三个字母。
  • 因此这个正则表达式所要匹配的内容为:文件名为纯小写字母的文本文件。
#include <iostream>
#include <string>
#include <regex>

int main() {
    std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
    // 在 C++ 中'\'会被作为字符串内的转义符,为使'\.'作为正则表达式传递进去生效,
    // 需要对'\'进行二次转义,从而有'\\.'
    std::regex txt_regex("[a-z]+\\.txt");
    for (const auto &fname: fnames)
        std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
}
// OUTPUT:
foo.txt: 1
bar.txt: 1
test: 0
a0.txt: 0
AAA.txt: 0

除此之外,还有一种常用的形式就是依次传入 std::string / std::smatch / std::regex 三个参数。

其中 std::smatch 的本质其实是 std::match_results,在标准库中,std::smatch 被定义为了 std::match_results,也就是一个子串迭代器类型的 match_results。使用 std::smatch 可以方便的对匹配的结果进行获取。

std::regex base_regex("([a-z]+)\\.txt");
std::smatch base_match;
for(const auto &fname: fnames) {
    if (std::regex_match(fname, base_match, base_regex)) {
        // sub_match 的第一个元素匹配整个字符串 ([a-z]+)\\.txt
        // sub_match 的第二个元素匹配了第一个括号表达式 ([a-z]+)
        if (base_match.size() == 2) {
            std::string base = base_match[1].str();
            std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
            std::cout << fname << " sub-match[1]: " << base << std::endl;
        }
    }
}
// OUTPUT
sub-match[0]: foo.txt
foo.txt sub-match[1]: foo
sub-match[0]: bar.txt
bar.txt sub-match[1]: bar

猜你喜欢

转载自blog.csdn.net/qq_38844835/article/details/119155619