C++11/14 高速上手教程

C++11/14 高速上手教程

教程简介:C++11 的出现及C++14 对 C++11的重要补充,让 C++ 这门古老的语言注入了新的活力。如果你还在使用传统 C++,打着 C++ 的名号写出 C 语言代码,请停下来。本教程将带领你快速上手 C++11/14 的重要特性。

本教程由欧龙崎整理发布在实验楼,全部系列教程及在线练习地址:C++ 11/14 高速上手教程

第一节、C++11/14 简介

一、概述

目标读者

本教程假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。

引言

C++ 算是一个用户群体比较大的语言了,从 C++98 到 C++11 经历了长达十年多之久的积累,C++14 则是作为对 C++11 的重要补充和优化,所有这些新标准中扩充的特性,给 C++ 这门语言注入了新的活力。

那些还在坚持使用传统 C++ (本教程把 C++98 及其之前的 C++ 特性均称之为传统 C++)而未接触过 C++11/14 的 C++ 程序员在见到诸如 Lambda 表达式这类全新特性时,甚至会流露出『学的不是同一门语言』的惊叹之情。

C++1x (本教程中指 C++11/14, 甚至 C++17) 为传统 C++ 注入的大量特性使得整个 C++ 变得更加像一门现代化的语言。C++1x 不仅仅增强了 C++ 语言自身的可用性,auto 关键字语义的修改使得我们更加有信心来操控极度复杂的模板类型。同时还对语言运行期进行了大量的强化,Lambda 表达式的出现让 C++ 具有了『匿名函数』的『闭包』特性,而这一特性几乎在现代的编程语言(诸如 Python/Swift/... )中已经司空见惯,右值引用的出现解决了 C++ 长期以来被人诟病的临时对象效率问题等等。

C++1x 为自身的标准库增加了非常多的工具和方法,诸如在语言层面上提供了 std::thread 支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨平台支持;std::regex提供了完整的正则表达式支持等等。

C++98 已经被实践证明了是一种非常成功的『范型』,而 C++1x 的出现,则进一步推动这种范型,让 C++ 成为系统程序设计和库开发更好的语言。

提示:本课程所有代码至少需要开启 -std=c++11 选项来支持 C++11 相关特性,在介绍 C++14 特性时的相关代码需要开启 -std=c++14 的编译选项,例如:

g++ main.cpp -std=c++11
g++ main.cpp -std=c++14

推荐所有代码均使用 -std=c++14 选项进行编译。

二、教程目录

本教程虽号称高速上手教程,但实际上对 C++11/14 的相关特性做了一个较为全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容,这从某种意义上来说,也算是高速上手了。

这些特性并不需要全部掌握,只需针对特定的应用场景,学习、查阅最适合自己的新特性即可(但总结部分依然给出了建议学习的特性)。

值得一提的是,本教程在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。

  • C++11/14 简介
    • 概述
    • 教程目录
    • 被弃用的特性
    • 与 C 的兼容性
  • 语言可用性的强化
    • nullptr 与 constexpr
    • 类型推导
      • auto
      • decltype
      • 尾返回类型、auto 与 decltype 配合
    • 区间迭代
      • 基于范围的 for 循环
    • 初始化列表
      • std::initializer_list
      • 统一初始化语法
    • 模板增强
      • 外部模板
      • 尖括号 >
      • 类型别名模板
      • 变长参数模板
    • 面向对象增强
      • 委托构造
      • 继承构造
      • 显式虚函数重载
        • override
        • final
      • 显式禁用默认函数
    • 强类型枚举
  • 语言运行期的强化
    • lambda 表达式
      • lambda 表达式基础
        • 值捕获
        • 引用捕获
        • 隐式捕获
        • 表达式捕获
      • 泛型 lambda
    • 函数对象包装器
      • std::function
      • std::bind/std::placeholder
    • 右值引用
      • 左值、右值的纯右值、将亡值、右值
      • 右值引用和左值引用
      • 移动语义
      • 完美转发
  • 对标准库的扩充: 新增容器
    • std::array
    • std::forward_list
    • std::unordered_set
    • std::unordered_map
    • std::tuple
      • 基本操作
      • 运行期索引
      • 合并与迭代
  • 对标准库的扩充: 智能指针和引用计数
    • 引用计数
    • std::shared_ptr
    • std::make_shared
    • std::unique_ptr
    • std::weak_ptr
  • 对标准库的扩充: 正则表达式库
    • 正则表达式简介
      • 普通字符
      • 特殊字符
      • 限定符
    • std::regex 及其相关
      • std::regex
      • std::regex_match
      • std::match_results
  • 对标准库的扩充: 语言级线程支持
    • std::thread
    • std::mutex
    • std::unique_lock
    • std::future
    • std::packaged_task
    • std::condition_variable
  • 其他杂项
    • 新类型
      • long long int
    • noexcept 的修饰和操作
    • 字面量
      • 原始字符串字面量
      • 自定义字面量
  • 扩展主题: C++17 简介
    • 主要入选特性
      • 非类型模板参数的 auto
      • std::variant<>
      • 结构化绑定(Structured bindings)
      • 变量声明的强化
    • 未入选特性
      • Concepts

三、被弃用的特性

在学习 C++1x 之前,我们先了解一下从 C++11 开始,被弃用的主要特性:

注意:弃用不等于废弃,只是用于暗示程序员这些特性将从未来的标准中消失,应该尽量避免使用。但是,已弃用的特性依然是标准库的一部分,并且出于兼容性的考虑,这些特性其实会『永久』保留。

  • 如果一个类有析构函数,为其生成拷贝构造函数和拷贝赋值运算符的特性被弃用了。

  • 不再允许字符串字面值常量赋值给一个 char *。如果需要用字符串字面值常量赋值和初始化一个 char *,应该使用 const char * 或者 auto。

char *str = "hello world!"; // 将出现弃用警告
  • C++98 异常说明、 unexcepted_handler、set_unexpected() 等相关特性被弃用,应该使用 noexcept。

  • auto_ptr 被弃用,应使用 unique_ptr。

  • register 关键字被弃用。

  • bool 类型的 ++ 操作被弃用。

  • C 语言风格的类型转换被弃用,应该使用 static_cast、reinterpret_cast、const_cast 来进行类型转换。

还有一些其他诸如参数绑定(C++11 提供了 std::bind 和 std::function)、export 等特性也均被弃用。前面提到的这些特性如果你从未使用或者听说过,也请不要尝试去了解他们,应该向新标准靠拢,直接学习新特性。毕竟,技术是向前发展的。

四、与 C 的兼容性

出于一些不可抗力、历史原因,我们不得不在 C++ 中使用一些 C 语言代码(甚至古老的 C 语言代码),例如 Linux 系统调用。在 C++11 出现之前,大部分人当谈及 『C 与 C++ 的区别是什么』时,普遍除了回答面向对象的类特性、泛型编程的模板特性外,就没有其他的看法了,甚至直接回答『差不多』,也是大有人在。下面的韦恩图大致上回答了 C 和 C++ 相关的兼容情况:

从现在开始,你的脑子里应该树立 『C++ 不是 C 的一个超集』这个观念(而且从一开始就不是)。在编写 C++ 时,也应该尽可能的避免使用诸如 void* 之类的程序风格。而在不得不使用 C 时,应该注意使用 extern "C" 这种特性,将 C 语言的代码与 C++代码进行分离编译,再统一链接这种做法,例如:

// foo.h
#ifdef __cplusplus
extern "C" {
#endif

int add(int x, int y);

#ifdef __cplusplus
}
#endif

// foo.c
int add(int x, int y) {
    return x+y;
}

// main.cpp
#include "foo.h"
int main() {
    add(1, 2);
    return 0;
}

应先使用 gcc 编译 C 语言的代码:

gcc -c foo.c

编译出 foo.o 文件,再使用 g++ 将 C++代码和 .o 文件链接起来(或者都编译为 .o 再统一链接):

g++ main.cpp foo.o -o main

本课程的全部系列教程及代码,可在实验楼查看并在线完成:C++11/14 高速上手教程

更多C++系列课程:C/C++全部 - 课程

发布于 2016-09-02

文章被以下专栏收录