目录
1. 结构化绑定声明 (Structured Bindings)
2. if/else语句中的初始化语句 (If/else with initialization)
4. 三向比较 (Spaceship Operator <=>)
6. 范围循环 (Range-based for loop with initializer)
7. 新的属性([[likely]] 和 [[unlikely]])
C++ 的发展总结:
C++ 是由 Bjarne Stroustrup 于 1979 年在贝尔实验室(Bell Labs)开始开发的,最初是作为 C 语言的一个扩展,目的是在不丧失 C 语言高效性的基础上,提供面向对象编程的特性。C++ 的发展历程可以分为以下几个重要阶段:
C++ 的早期发展 | 1979-1985 |
C++ 标准化过程 | 1985-1998 |
C++ 标准演化 | 2003-2011 |
C++11 | 2011年 |
C++14 | 2014年 |
C++17 | 2017年 |
C++20 | 2020年 |
C++23 | 2023年 |
1. C++ 的早期发展(1979-1985)
- 1979年:C++ 的前身 "C with Classes" 出现。Bjarne Stroustrup 在贝尔实验室的工作中,设计了一个扩展 C 语言的工具,该工具为 C 语言增加了类(class)、构造函数和析构函数等面向对象特性。它的设计目标是提高程序的可重用性,并通过封装数据和方法的方式增强程序结构。
- 1983年:C with Classes 更名为 **C++**,并发布了第一版 C++ 语言,开始引入类、继承、多态等面向对象的特性,虽然这些特性并不完善,但已经为后来的发展奠定了基础。
- 1985年:C++ 语言的第一个标准版本发布。Stroustrup 为此编写了《C++ Programming Language》一书,详细介绍了 C++ 的语法、特性和编程模型。
2. C++ 标准化过程(1985-1998)
- 1989年:C++ 的第一个国际标准草案发布,作为一种较为成熟的语言,逐渐被越来越多的开发者使用。
- 1990年:C++ 被纳入国际标准化组织(ISO)的标准化过程。这个过程中,C++ 逐步加入了更多的语言特性,如模板、异常处理、名字空间等。
- 1998年:C++98 标准发布,这是 C++ 语言的第一个正式标准版本。C++98 通过增加模板支持、标准库(如 STL)和异常处理机制,使得 C++ 语言更加全面和功能强大,成为当时最主流的编程语言之一。
3. C++ 标准演化(2003-2011)
- 2003年:C++03 标准发布,主要是对 C++98 标准的修正和小幅改进,修复了一些语言特性中的小错误,并未引入重大的新特性。
- 2007年:ISO 发布了 C++0x 标准草案,该标准最终演变为 C++11。在 C++0x 的过程中,许多新的特性开始得到提出和采纳,如 自动类型推导、右值引用、lambda 表达式、并发编程 等。C++11 的标准化过程历时多年,经历了大量的讨论和修改。
4. C++11(2011年)
- 2011年:C++11 正式发布,它是 C++ 语言的一个重大变革,包含了大量的新特性,标志着 C++ 语言进入了一个全新的时代。C++11 的主要特性包括:
- 自动类型推导(
auto
); - 右值引用和移动语义(
&&
、std::move
); - Lambda 表达式;
- 线程支持(
std::thread
); - 智能指针(
std::unique_ptr
、std::shared_ptr
); - 类型推导(
decltype
)等。
- 自动类型推导(
- C++11 的引入极大提升了 C++ 语言的表达力和现代化程度,使得 C++ 编程更具灵活性和高效性。
5. C++14(2014年)
- 2014年:C++14 发布,相较于 C++11,C++14 更像是一个小规模的优化和修复版本。C++14 主要增强了 C++11 中的特性,如 Lambda 表达式的泛型支持、
std::make_unique
的引入、decltype(auto)
的改进等。此外,还修复了一些编译器和实现中的问题。 - C++14 让 C++11 中的很多新特性变得更加稳定,增加了可编程性和工具支持。
6. C++17(2017年)
- 2017年:C++17 发布,这个版本包含了一些有意义的语言特性和标准库增强。主要特性包括:
- **
std::optional
**:用于表示可能为空的值; - 结构化绑定声明(
auto [a, b] = tuple;
); if
和switch
中的初始化语句;- **
std::filesystem
**:用于处理文件和目录的标准库; - **改进的
std::string
和std::array
**; - 并行算法(
std::for_each
等支持并行执行)。
- **
- C++17 不仅增强了语言本身的功能,还进一步加强了对现代硬件和多核处理器的支持,增强了性能和并行计算能力。
7. C++20(2020年)
- 2020年:C++20 发布,是 C++ 语言历史上一个非常重要的版本,带来了许多令人兴奋的新特性:
- 模块(Modules):改善头文件管理,减少编译时间;
- 三方比较操作符(
<=>
): 用于自动生成比较操作符(比如==
,!=
,<
,<=
,>
,>=
); - 协程(Coroutines):使得异步编程更加简洁;
- 概念(Concepts):提供更强的类型约束机制,使模板编程更加安全;
- 范围 for 循环改进:更简洁的语法和强大的灵活性;
- **
std::span
**:处理数组和内存块的轻量级对象; - 改进的标准库:包括
std::format
、std::ranges
、std::calendar
等。
- C++20 为 C++ 带来了大量的现代化特性,并进一步提升了语言的表达能力、性能和并发编程的支持。
8. C++23(2023年)
- 2023年:C++23 发布,虽然其变化相较于 C++20 比较平稳,但仍然包含了一些重要的改进和修复:
- 新的标准库功能:例如,
std::expected
、std::generator
和std::ranges
的进一步改进; - 语言增强:对模板和类型推导的改进,增强了语言的灵活性和可读性;
- 性能优化:进一步增强编译器对并行、异步和内存管理的支持。
- 新的标准库功能:例如,
- C++23 标志着 C++ 语言持续向现代化发展,并且对性能和并发编程的支持进一步加强。
-
从面向过程到面向对象:最初的 C++ 语言只是在 C 语言的基础上增加了类和面向对象的特性,经过多年发展,C++ 在保留 C 语言高效性的同时,也吸收了面向对象、泛型编程和函数式编程的优势,成为了一门多范式的语言。
-
标准化过程:从 C++98 到 C++11,再到 C++14、C++17 和 C++20,C++ 逐渐成为一种具有广泛应用的编程语言,每个版本都带来了一些新的特性和增强,逐步适应现代软件开发的需求。
-
现代化与性能:C++ 在引入面向对象特性后,也逐步加入了并行计算、内存管理、泛型编程、协程等现代编程特性,使得它在高性能计算、系统开发、游戏开发和金融等领域仍然占有一席之地。
C++ 的发展历程体现了它强大的灵活性和持久生命力,C++ 不断演进,始终保持着与时俱进的能力,成为全球使用最广泛的编程语言之一。
C++ 的发展历程可以分为多个版本阶段,每个版本都带来了新的语言特性、库的改进和标准的增强。下面是 C++ 各个版本的主要变化和特性介绍:
1. C++ 1985: C++ 1.0
- 最初版本:由贝尔实验室的 Bjarne Stroustrup 在 1980 年代初期开发。
- 主要特点:
- 在 C 语言的基础上加入了面向对象编程(OOP)特性。
- 支持类、继承、多态、封装等面向对象的基本特性。
- 引入了函数和运算符重载。
C++ 1.0 是由 Bjarne Stroustrup 在贝尔实验室开发的第一版 C++ 编程语言。它是在 C 语言的基础上加入了面向对象编程(OOP)的特性,因此也被称为是“C with Classes”(C 带类)。C++ 1.0 并不是一个标准化的语言版本,而是一个实验性语言,它为后来的 C++ 标准奠定了基础。
主要特性:
-
类与对象:C++ 1.0 引入了面向对象编程的核心概念,最初的类和对象支持比较简单。
- 类是 C++ 中的基本构造,用于封装数据和功能。
- 对象是类的实例,表示实际的数据和行为。
-
函数和运算符重载:C++ 1.0 支持函数重载和运算符重载,使得程序员可以根据需要定义不同版本的函数或操作符。
-
基本继承:C++ 1.0 支持从一个类继承另一个类,使得可以构建更为复杂的类结构。
-
无标准库:当时的 C++ 语言没有像现代 C++ 那样的标准库。数据结构和算法通常依赖开发者自己编写。
示例代码:
C++ 1.0 版本的代码相对简单。以下是一个典型的 C++ 1.0 示例,演示了类的定义、对象创建、继承以及运算符重载。
#include <iostream>
using namespace std;
// 定义一个类
class Box {
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
public:
// 构造函数
Box(double l, double b, double h) {
length = l;
breadth = b;
height = h;
}
// 计算体积的方法
double volume() {
return length * breadth * height;
}
// 运算符重载
Box operator+(const Box& b) {
// 返回一个新的 Box 对象,体积相加
return Box(length + b.length, breadth + b.breadth, height + b.height);
}
};
// 主函数
int main() {
// 创建对象
Box box1(3.5, 1.2, 2.0);
Box box2(1.0, 2.0, 3.0);
// 计算并输出 box1 的体积
cout << "Box1 Volume: " << box1.volume() << endl;
// 运算符重载使用示例
Box box3 = box1 + box2;
cout << "Box3 Volume (box1 + box2): " << box3.volume() << endl;
return 0;
}
代码讲解:
- **类
Box
**:定义了一个包含length
、breadth
和height
的类,用于表示一个盒子,并且提供了一个计算体积的方法volume()
。 - 构造函数:用于初始化
Box
对象,设置它的长度、宽度和高度。 - 运算符重载:使用
operator+
重载运算符+
,使得两个Box
对象相加时,返回一个新的Box
对象,长度、宽度和高度分别相加。 - 在
main()
中创建对象:通过构造函数创建了box1
和box2
,并计算它们的体积。在计算box3
的体积时,使用了重载的加法运算符。
C++ 1.0 的局限性:
- 缺少模板:当时的 C++ 并不支持模板(泛型编程),模板编程是后来在 C++ 1990 和 1998 标准中引入的。
- 没有标准库:没有现代 C++ 的 STL(标准模板库),因此没有常用的容器、算法等功能。
- 继承的支持较为基础:C++ 1.0 支持继承,但还没有引入多态、虚函数等更高级的特性。
总结:
C++ 1.0 是 C++ 语言的初步版本,它为面向对象编程提供了基础设施,并通过类、对象、继承、重载等功能扩展了 C 语言的功能。虽然当时的 C++ 功能较为简单,但它为后续版本的语言特性打下了基础,并引领了 C++ 向更强大的方向发展。
2. C++ 1989: C++ 2.0
- 增强功能:
- 多重继承:支持多个基类的继承。
- 虚继承:解决多重继承中的“菱形继承”问题,确保继承层次中的基类只有一份副本。
- 抽象类:引入了纯虚函数(抽象类)的概念,使得类能够定义接口。
C++ 2.0 是 C++ 语言的第二个重要版本,发布于 1989 年。C++ 2.0 在 C++ 1.0 的基础上进行了重要的扩展和改进,引入了多个新特性,特别是 多重继承 和 虚继承。这些新特性使得 C++ 的面向对象编程能力得到了显著增强。C++ 2.0 为后来的 C++ 语言标准奠定了许多重要基础。
主要特性:
- 多重继承:C++ 2.0 引入了多重继承的支持,使得一个类可以继承多个基类,从而可以组合多个类的功能。
- 虚继承:为了解决多重继承带来的“菱形继承”问题(即一个类通过多个继承路径继承同一个基类),C++ 2.0 引入了 虚继承 的概念。虚继承确保通过多个继承路径继承的同一个基类只有一份副本。
- 虚函数:继续扩展了对面向对象编程的支持,尤其是通过 虚函数 机制,使得 C++ 支持 运行时多态。
- 更强的类型检查:C++ 2.0 对类型检查进行了增强,使得语言的类型系统更加严格和安全。
多重继承与虚继承
C++ 2.0 的最大变化之一就是支持 多重继承,并且引入了 虚继承 来解决多重继承可能引起的问题。在多重继承中,如果一个类继承自多个基类,并且这些基类有公共的祖先类,那么在派生类中会有重复的祖先类副本,这时就可能出现问题。虚继承解决了这一问题,通过确保只有一个祖先类副本,从而避免了数据冗余和潜在的错误。
示例代码:
以下是一个简单的示例,演示了 多重继承 和 虚继承 的概念。
#include <iostream>
using namespace std;
// 基类A
class A {
public:
A() { cout << "Class A constructor" << endl; }
void showA() { cout << "Class A" << endl; }
};
// 基类B
class B {
public:
B() { cout << "Class B constructor" << endl; }
void showB() { cout << "Class B" << endl; }
};
// 基类C,虚继承A
class C : virtual public A {
public:
C() { cout << "Class C constructor" << endl; }
void showC() { cout << "Class C" << endl; }
};
// 派生类D,虚继承A和B
class D : public C, public B {
public:
D() { cout << "Class D constructor" << endl; }
void showD() { cout << "Class D" << endl; }
};
int main() {
// 创建派生类对象D
D d;
d.showA(); // 来自基类A
d.showB(); // 来自基类B
d.showC(); // 来自基类C
d.showD(); // 来自派生类D
return 0;
}
代码讲解:
- 类 A 和 B:
A
和B
是两个基类,都有自己的构造函数和成员函数。A
有一个showA()
函数,B
有一个showB()
函数。 - 类 C:
C
类继承了A
类,并使用virtual
关键字进行 虚继承。这样,在C
的子类中,如果有多个继承自A
的路径,它们会共享同一个A
类副本,避免了重复数据。 - 类 D:
D
类从C
和B
继承,并且通过虚继承机制共享了A
类的副本。 - 输出:
- 当
D
类的对象被创建时,首先调用A
、B
和C
的构造函数,再调用D
的构造函数。 showA()
、showB()
和showC()
分别调用了从基类继承的成员函数。
- 当
运行输出:
Class A constructor
Class B constructor
Class C constructor
Class D constructor
Class A
Class B
Class C
Class D
解释:
- 虚继承:
C
类通过virtual public A
虚继承了A
类,保证了通过D
类的继承关系,A
类只有一份副本。即使D
类是通过B
和C
两个路径继承A
,A
类也只会被构造一次。 - 多重继承:
D
类通过多重继承继承了B
和C
,因此D
类拥有B
和C
类的功能。
C++ 2.0 的局限性:
- 没有命名空间:C++ 2.0 并没有引入命名空间(namespace),因此容易出现命名冲突的问题。
- 没有模板支持:模板编程(如函数模板和类模板)在 C++ 2.0 中还没有出现,这一特性是在 C++ 3.0 中引入的。
- 继承体系较为简化:虽然支持多重继承和虚继承,但对继承体系的支持仍较为基础,随着 C++ 后续版本的发布,继承和多态的功能会得到更完善的支持。
总结:
C++ 2.0 通过引入 多重继承 和 虚继承 大大增强了 C++ 的面向对象能力,使得 C++ 成为一种更强大的编程语言。这些特性让 C++ 能够支持更复杂的类层次结构,解决了多重继承中的一些难题。虽然 C++ 2.0 语言仍然很基础,但它为 C++ 后续版本的更高级特性,如模板和更复杂的类型系统,奠定了基础。
3. C++ 1990: C++ 3.0
- 功能增强:
- 模板:引入了模板(函数模板和类模板),开始支持泛型编程。
- 标准库:初步支持了标准模板库(STL)的概念,虽然 STL 直到后来的版本才成为标准。
C++ 3.0 是 C++ 语言的第三个版本,发布于 1990 年。相比于前两个版本,C++ 3.0 在语言特性和功能上做出了重要的扩展,最为关键的就是引入了 模板 的支持,这是 C++ 语言向 泛型编程 过渡的重要一步。模板的引入使得 C++ 支持编写与类型无关的代码,从而使得编程变得更加灵活和强大。
主要特性:
-
模板(Templates):
- 函数模板:允许定义可以接受不同类型参数的函数。
- 类模板:允许定义可以接受不同类型参数的类。模板是 C++ 语言中泛型编程的基础。
-
增强的类型系统:
- 支持更复杂的类型定义和类型推导。
- 改进了类和对象的类型推断机制。
-
基本的标准库(STL的雏形):
- 尽管 C++ 3.0 本身并没有正式定义标准模板库(STL),但它开始支持类似 STL 的一些容器和算法概念,为未来 STL 的引入奠定了基础。
-
支持虚函数、运算符重载等特性:
- C++ 3.0 继续扩展了面向对象编程的支持,增强了对继承、多态、运算符重载等特性的支持。
模板的引入
C++ 3.0 的核心改进就是引入了模板机制,使得函数和类能够接受参数类型作为模板参数。这使得 C++ 能够支持泛型编程,从而能够编写类型安全且重用性更高的代码。
示例代码:模板示例
以下是一个简单的示例,展示了如何在 C++ 3.0 中使用 函数模板 和 类模板。
#include <iostream>
using namespace std;
// 函数模板:计算两个数字的最大值
template <typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
// 类模板:定义一个简单的容器类
template <typename T>
class Box {
private:
T value;
public:
Box(T v) : value(v) {}
T getValue() { return value; }
void setValue(T v) { value = v; }
};
int main() {
// 使用函数模板
int x = 10, y = 20;
cout << "Max of x and y: " << getMax(x, y) << endl;
double a = 3.14, b = 2.71;
cout << "Max of a and b: " << getMax(a, b) << endl;
// 使用类模板
Box<int> intBox(100);
cout << "Box contains: " << intBox.getValue() << endl;
intBox.setValue(200);
cout << "Updated Box contains: " << intBox.getValue() << endl;
Box<double> doubleBox(3.14159);
cout << "Box contains: " << doubleBox.getValue() << endl;
return 0;
}
代码讲解:
-
**函数模板
getMax
**:getMax
是一个函数模板,它接受两个类型为T
的参数,返回它们中的最大值。T
是一个类型参数,可以是任何数据类型(如int
、double
等),当调用模板函数时,编译器会根据传入的参数类型推导出T
的具体类型。
-
**类模板
Box
**:Box
是一个类模板,它有一个类型为T
的成员变量value
。- 构造函数接受一个
T
类型的参数来初始化value
,并提供了getValue
和setValue
方法来访问和修改value
。
运行输出:
Max of x and y: 20
Max of a and b: 3.14
Box contains: 100
Updated Box contains: 200
Box contains: 3.14159
代码解释:
getMax
函数模板:在main()
中,分别使用了int
和double
类型的参数来调用getMax
函数模板。编译器根据传入的参数类型推导出T
的类型。Box
类模板:Box<int>
和Box<double>
分别是使用int
和double
类型的类模板实例化出来的对象。每个Box
对象可以存储不同类型的数据。
模板的优势:
- 代码重用:通过模板,函数和类可以支持多种不同的类型,而不需要为每个类型编写重复的代码。
- 类型安全:模板在编译时进行类型检查,确保类型的正确性,避免了运行时的类型错误。
C++ 3.0 的局限性:
- 模板特性较为基础:C++ 3.0 中的模板机制较为简单,没有现代 C++ 中复杂的模板特性,如模板特化、SFINAE(Substitution Failure Is Not An Error)等。
- 标准库仍然不成熟:尽管模板提供了更多的灵活性,但标准库并没有像 C++11 和 C++14 中那样强大,STL 的概念才刚刚开始萌芽。
总结:
C++ 3.0 是 C++ 语言的一个重要版本,模板的引入是其最大的亮点,标志着 C++ 进入了泛型编程的时代。模板让 C++ 变得更加灵活,支持更加高效的代码重用和类型安全的操作。通过函数模板和类模板,C++ 可以在不同类型之间共享代码,从而极大地提高了开发效率和代码的可维护性。虽然 C++ 3.0 的模板机制相对简单,但它为后续的 C++ 标准化进程和更复杂的模板特性奠定了基础。
4. C++ 1998: C++98
- C++ 标准化:C++98 是 C++ 的第一个正式标准版本,由 ISO/IEC 14882:1998 定义。
- 主要特性:
- STL(标准模板库):引入了标准模板库,包括
vector
、list
、map
、set
等容器,及算法(如排序、查找等)。map - C++ Reference set - C++ Reference - 命名空间(namespace):为了避免名称冲突,引入了命名空间。
- 异常处理:引入了
try
、catch
、throw
异常处理机制。 - RTTI(运行时类型信息):引入了
typeid
和dynamic_cast
,允许运行时识别对象的类型。
- STL(标准模板库):引入了标准模板库,包括
C++98 是 C++ 语言的第一个国际标准,正式由 ISO(国际标准化组织)在 1998 年发布。C++98 语言标准继承了 C++ 3.0 的核心特性,并在此基础上进行了多个重要的改进和规范化。这个版本的 C++ 为标准库(STL)提供了规范,并开始支持更多的现代编程特性。
主要特性:
-
模板:
- C++98 中的模板特性得到了进一步的增强,支持函数模板和类模板,并且模板的使用变得更加广泛。模板参数可以是类、函数、指针等类型。
-
标准模板库(STL)
-
异常处理:
- C++98 提供了标准化的异常处理机制,使用
try
,catch
,throw
语句来处理运行时错误。
- C++98 提供了标准化的异常处理机制,使用
-
命名空间:
- C++98 引入了 命名空间(
namespace
),用于避免名称冲突,尤其是在大型项目中。
- C++98 引入了 命名空间(
-
虚函数与多态:
- C++98 继续强化了面向对象编程的支持,特别是对 虚函数 和 多态 的使用。
-
const 和引用:
- C++98 对
const
关键字和引用进行了严格的规范,使得编程更加安全。
- C++98 对
-
函数指针和类型转换:
- 提供了对 函数指针 和 类型转换(如
static_cast
,dynamic_cast
等)的支持,增强了 C++ 的灵活性。
- 提供了对 函数指针 和 类型转换(如
-
类成员的类型推导:
- C++98 引入了对类成员类型的推导,允许在某些情况下省略类型声明。
示例代码:C++98 的常见特性
以下是一个综合示例,展示了 C++98 中的几个核心特性,包括模板、标准库(STL)、命名空间和异常处理。
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdexcept>
namespace MyNamespace {
// 模板函数:计算两个数的最大值
template <typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
// 使用 STL 容器:vector 和算法
void useSTL() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
std::sort(vec.begin(), vec.end());
std::cout << "Sorted vector: ";
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
}
// 异常处理示例
void testException() {
try {
throw std::out_of_range("Index out of range!");
} catch (const std::exception& e) {
std::cout << "Caught exception: " << e.what() << std::endl;
}
}
}
int main() {
// 使用命名空间
using namespace MyNamespace;
// 使用模板函数
int x = 10, y = 20;
std::cout << "Max of x and y: " << getMax(x, y) << std::endl;
// 使用 STL 容器和算法
useSTL();
// 测试异常处理
testException();
return 0;
}
代码讲解:
-
**模板函数
getMax
**:- 这是一个简单的模板函数,接受两个相同类型的参数,并返回它们中的最大值。
typename T
表示这是一个模板函数,T
是占位符类型,可以在调用时由编译器自动推导出。
- 这是一个简单的模板函数,接受两个相同类型的参数,并返回它们中的最大值。
-
STL 容器和算法:
- 在
useSTL
函数中,使用了std::vector
来存储整数数据,并使用std::sort
算法对数据进行了排序。std::vector
是 C++98 标准库中的一个动态数组容器,std::sort
是一个算法函数,用于对容器中的元素进行排序。
- 在
-
异常处理:
testException
函数演示了 C++98 的异常处理机制。在这个函数中,抛出了一个std::out_of_range
异常,并在catch
块中捕获它,输出异常的描述信息。
运行输出:
Max of x and y: 20
Sorted vector: 1 1 2 3 3 4 5 5 5 6 9
Caught exception: Index out of range!
代码解释:
- **模板
getMax
**:通过模板,可以在运行时自动确定T
的类型,支持不同的数据类型(如int
、double
等)。在本例中,它比较了两个整数x
和y
。 - **STL 容器
std::vector
和std::sort
**:std::vector
是一个可以动态调整大小的容器,它存储一系列元素。std::sort
是一个通用算法,用来对容器中的元素进行排序。 - 异常处理:C++98 提供了标准的异常处理机制,允许在代码运行过程中抛出和捕获异常,避免程序因错误而崩溃。
C++98 的局限性:
- 没有
auto
关键字:C++98 中没有auto
关键字,因此类型推导只能依靠显式的类型声明。 - **没有
nullptr
**:C++98 中没有nullptr
,指针使用时可能会引发隐式的类型转换问题。 - 没有类型推导的
for
循环:C++98 中没有像 C++11 中那样的基于范围的for
循环(for (auto& elem : container)
)。 - 模板元编程有限:虽然模板功能在 C++98 中已被引入,但对于模板元编程的支持相对有限,模板编程的复杂性和灵活性较低。
总结:
C++98 是 C++ 语言的第一个国际标准版本,它巩固并标准化了之前 C++3.0 中的许多特性,并加入了 模板 和 STL 等重要特性,使得 C++ 在实际开发中更加高效和灵活。通过模板,C++98 使得代码可以在不同的数据类型之间重用,同时标准库(STL)提供了高效的容器和算法支持,大大提高了开发效率。虽然 C++98 为后来的 C++ 标准奠定了基础,但它在某些领域(如模板编程和自动类型推导)仍存在局限。
5. C++ 2003: C++03
- 修复和改进:
- 小的改进:C++03 主要是对 C++98 标准的一些修订和错误修正,没有引入重大新特性。
- 支持一些新的编译器特性,如增强的模板支持、异常安全性等。
C++03 是 C++ 语言的一个小版本更新,它是对 C++98 的修正版本,主要用于解决一些语言上的不一致性和设计上的小问题。C++03 并未引入许多新的功能,而是对 C++98 标准中的一些细节进行了修正和澄清,确保语言的一致性和规范性。这个版本主要集中在语言规范的完善和一些编译器支持方面。
C++03 的发布并没有像 C++98 到 C++11 之间的版本那样进行革命性的更新,因此它在功能上与 C++98 并无太大差异。主要的更新包括对 模板特化、SFINAE(Substitution Failure Is Not An Error)等方面的细节修正,确保了编译器能够更好地支持标准库。
主要特性和变化:
-
模板的修正和改进:
- C++03 对模板特化和模板元编程做了小的修正,使得编译器对模板的支持更加一致。
- 改进了模板在某些边界情况中的行为,避免了 C++98 中一些不一致或令人困惑的部分。
-
标准库的修正:
- C++03 对标准库中的一些接口做了微调,解决了 C++98 标准库中的一些小问题。例如,修复了
std::vector
在某些情况下的行为不一致性,确保了 STL 中的算法与容器的更好兼容性。
- C++03 对标准库中的一些接口做了微调,解决了 C++98 标准库中的一些小问题。例如,修复了
-
修复了常见的编译器问题:
- C++03 对一些编译器的兼容性问题进行了修复,确保不同厂商的编译器之间有更好的兼容性。
-
去除了某些不再使用的特性:
- C++03 删除了一些在 C++98 中冗余或未被广泛使用的特性,从而使标准更加精简和一致。
C++03 与 C++98 的区别:
- 细节修正:C++03 主要是针对 C++98 标准中存在的某些歧义性和编译器实现差异进行修正。它并没有加入新的语言特性,而是专注于优化语言和标准库的现有特性。
- STL 和模板的完善:C++03 解决了模板和标准库的一些小问题,确保标准库在不同编译器下的行为一致。
示例代码:C++98 和 C++03 的差异
由于 C++03 与 C++98 的差异主要体现在语言的细节上,因此以下示例代码在 C++03 中的表现与 C++98 是相同的。我们展示一些在 C++98 和 C++03 中模板使用的常见问题,这些问题在 C++03 中得到了修正。
示例代码:模板特化修复和 SFINAE
改进
#include <iostream>
#include <type_traits>
// 模板类的默认实现
template <typename T>
class Printer {
public:
static void print(const T& value) {
std::cout << value << std::endl;
}
};
// 特化:对于 const 类型的处理
template <typename T>
class Printer<const T> {
public:
static void print(const T& value) {
std::cout << "const value: " << value << std::endl;
}
};
// 使用 SFINAE 来选择函数
template <typename T>
typename std::enable_if<!std::is_integral<T>::value, void>::type
printType(const T& value) {
std::cout << "Non-integer value: " << value << std::endl;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
printType(const T& value) {
std::cout << "Integer value: " << value << std::endl;
}
int main() {
Printer<int>::print(123); // 默认模板
Printer<const int>::print(456); // 特化模板
printType(123); // 调用整数版本
printType(3.14); // 调用非整数版本
return 0;
}
代码解释:
-
模板类
Printer
的特化:Printer
是一个模板类,在默认情况下,它的print
函数打印任意类型的值。- 特化版本
Printer<const T>
对const
类型的处理进行了特殊化,使其输出时带有 "const value: " 字样。此特化是在 C++03 中明确规定的,确保了特化行为的统一性。
-
SFINAE(Substitution Failure Is Not An Error):
-
printType
函数使用了 SFINAE 来根据类型T
的不同选择不同的版本。 -
使用
std::enable_if
和std::is_integral
来判断T
是否为整数类型,并分别调用不同的函数。SFINAE
机制可以避免无效的模板实例化,确保了代码的类型安全。 -
如果
T
是整数类型,printType(int)
会被调用;否则,printType(double)
会被调用。
-
运行输出:
123
const value: 456
Integer value: 123
Non-integer value: 3.14
C++03 与 C++98 的局限性:
- 没有新的语言特性:C++03 仅仅是 C++98 的修正版本,没有引入新的语言特性。开发者并未从中获得像 C++11、C++14、C++17 那样的重大进展。
- 标准库的改进有限:虽然 C++03 修复了一些标准库的问题,但与后来的 C++ 标准相比,它的功能仍显得较为简单。
- 没有现代化工具:如
auto
、nullptr
、范围for
循环等特性都没有出现在 C++03 中,这使得它在一些现代编程需求面前显得有些过时。
总结:
C++03 是一个对 C++98 的修正版本,主要修复了一些语言规范中的小错误和不一致性,改进了模板、STL 和编译器的兼容性。它并没有引入新的语言特性,因此在实际使用中,C++98 和 C++03 的差异并不显著。C++03 主要对 C++98 的一些不明确之处进行了修正,为后续 C++ 标准的进化(如 C++11、C++14)奠定了基础。对于开发者来说,C++03 更多的是一种稳定性和兼容性更新,而不是一种功能性扩展。
6. C++ 2011: C++11
- 现代 C++ 的起点:C++11 是 C++ 的一次重大更新,被广泛认为是 C++ 语言的“现代化”转折点。
- 关键特性:
- 自动类型推断(
auto
):使用auto
关键字可以让编译器推断变量类型。- 智能指针:引入了
std::unique_ptr
、std::shared_ptr
、std::weak_ptr
,用于自动管理内存,避免内存泄漏。- Lambda 表达式:允许定义匿名函数,可以在代码中更方便地使用函数对象。
- 并发支持:引入了
<thread>
库,允许多线程编程。- **
nullptr
**:引入了nullptr
关键字,替代NULL
,提高代码的类型安全。- 右值引用和移动语义:引入右值引用(
&&
)和std::move
,提高对象传递效率,减少不必要的拷贝。- 范围 for 循环:引入了简洁的范围
for
循环,简化容器遍历。- 类型别名(
using
):引入using
关键字,简化类型别名定义。- 变长模板(Variadic Templates):支持可变参数模板,提高模板的灵活性。
C++11 是 C++ 语言的一个重大更新,正式于 2011 年发布。它引入了大量新的特性和改进,使 C++ 语言更现代化、更加高效和易于使用。C++11 标准的推出,极大地扩展了 C++ 的功能,使得 C++ 开发变得更加灵活、简洁,同时还提升了性能。
主要新特性:
-
自动类型推导(
auto
):auto
关键字允许编译器根据表达式的类型自动推导变量的类型,减少了类型声明的冗余,提高了代码可读性。
-
右值引用与移动语义(
&&
和std::move
):- 引入右值引用(
&&
)和std::move
,实现了 移动语义。通过移动资源而不是复制,显著提高了效率,尤其在需要大量数据传输的场合(如容器操作)。
- 引入右值引用(
-
范围
for
循环:- 引入了范围
for
循环(range-based for loop),简化了容器元素的遍历,避免了传统迭代器的复杂性。
- 引入了范围
-
Lambda 表达式:
- Lambda 表达式允许在函数内定义匿名函数,并且可以捕获外部变量,极大地增强了函数式编程的支持。
-
**
nullptr
**:nullptr
替代了NULL
,它是一个类型安全的空指针常量,消除了NULL
的类型不匹配问题。
-
**
constexpr
**:constexpr
使得可以在编译时求值的常量表达式更加简洁,增强了编译期计算的能力。
-
并发编程:
<thread>
库:- C++11 引入了原生的线程支持,
<thread>
库使得多线程编程变得更加容易和高效。
- C++11 引入了原生的线程支持,
-
类型别名(
using
):using
关键字可以用来为类型定义别名,取代了传统的typedef
,语法更加简洁。
-
强类型枚举(
enum class
):- 引入了强类型枚举
enum class
,避免了传统枚举可能产生的隐式转换和命名冲突问题。
- 引入了强类型枚举
-
**新标准库:
std::chrono
**:- C++11 引入了用于处理时间和日期的新标准库
std::chrono
,使得处理时间变得更加直观和精确。
- C++11 引入了用于处理时间和日期的新标准库
示例代码:C++11 特性
下面的代码示例展示了 C++11 的一些关键特性。
#include <iostream>
#include <vector>
#include <algorithm>
#include <thread>
#include <functional>
// 1. 自动类型推导
void autoExample() {
auto x = 10; // 编译器自动推导类型为 int
auto y = 3.14; // 编译器自动推导类型为 double
std::cout << "x: " << x << ", y: " << y << std::endl;
}
// 2. 范围 for 循环
void rangeBasedFor() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto& num : vec) { // 使用范围 for 遍历
num *= 2;
}
std::cout << "Doubled values: ";
for (const auto& num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
}
// 3. Lambda 表达式
void lambdaExample() {
std::vector<int> vec = {1, 2, 3, 4, 5};
int sum = 0;
// 使用 lambda 表达式进行求和
std::for_each(vec.begin(), vec.end(), [&sum](int num) { sum += num; });
std::cout << "Sum of elements: " << sum << std::endl;
}
// 4. 移动语义和右值引用
class MyClass {
public:
MyClass() { std::cout << "Constructor\n"; }
MyClass(const MyClass&) { std::cout << "Copy Constructor\n"; }
MyClass(MyClass&&) { std::cout << "Move Constructor\n"; }
MyClass& operator=(const MyClass&) { std::cout << "Copy Assignment\n"; return *this; }
MyClass& operator=(MyClass&&) { std::cout << "Move Assignment\n"; return *this; }
};
void moveExample() {
MyClass a;
MyClass b = std::move(a); // 使用移动构造函数
}
int main() {
// 自动类型推导
autoExample();
// 范围 for 循环
rangeBasedFor();
// Lambda 表达式
lambdaExample();
// 移动语义
moveExample();
// 使用线程
std::thread t([]{ std::cout << "Hello from a thread!\n"; });
t.join(); // 等待线程执行完毕
return 0;
}
代码解释:
-
**自动类型推导 (
auto
)**:- 使用
auto
关键字,编译器会自动推导变量x
和y
的类型,简化了类型声明,特别是对于复杂类型(如迭代器和函数返回类型)时非常有用。
- 使用
-
范围
for
循环:- 使用 C++11 引入的范围
for
循环来遍历容器。这个语法简洁、直观,避免了显式的迭代器操作。
- 使用 C++11 引入的范围
-
Lambda 表达式:
- 使用
std::for_each
算法与 lambda 表达式结合,计算容器中所有元素的总和。lambda 表达式[&sum](int num) { sum += num; }
捕获外部变量sum
并对容器中的每个元素执行加法操作。
- 使用
-
右值引用与移动语义:
- 在
moveExample
中,通过std::move
实现了对象a
的移动,将其资源转移给对象b
,触发了移动构造函数而不是拷贝构造函数。
- 在
-
线程支持:
- 使用 C++11 的
<thread>
库创建和启动一个线程,并且通过join()
等待线程执行完成。std::thread
简化了多线程编程。
- 使用 C++11 的
运行输出:
x: 10, y: 3.14
Doubled values: 2 4 6 8 10
Sum of elements: 15
Move Constructor
Hello from a thread!
C++11 的优势:
- 现代化的语法和功能:C++11 引入的众多新特性大大提升了语言的灵活性和表达力,减少了冗余代码,使得 C++ 编程更加简洁、易读和高效。
- 性能优化:通过右值引用和移动语义,C++11 使得许多数据传输和复制操作得以高效地优化,特别是在容器和大型对象的操作中,性能大幅提升。
- 并发支持:C++11 将多线程支持纳入语言标准,使得并发编程变得更加容易和可靠,减少了对外部库的依赖。
C++11 的局限性:
- 学习曲线:尽管 C++11 引入了许多现代化特性,但对一些开发者来说,掌握这些新特性可能需要一定的学习时间。
- 编译器支持问题:尽管大部分现代编译器已经完全支持 C++11,但一些旧的编译器或开发环境可能存在兼容性问题。
总结:
C++11 是 C++ 语言的一次重大更新,带来了许多新的语言特性和库支持,使得 C++ 编程变得更加简洁、强大和高效。无论是在性能优化、现代语法的引入,还是在并发支持等方面,C++11 都大幅提升了语言的可用性和表达能力。
7. C++ 2014: C++14
- 小幅增强:
constexpr
的改进:支持更复杂的编译时常量计算。- 二进制字面量:引入了支持二进制常量字面量。
auto
的增强:使得auto
可以推导更加复杂的类型。- Lambda 表达式的改进:支持更强的捕获特性(如捕获
*this
引用)。
C++14 是 C++ 语言的一个相对较小的更新版本,于 2014 年发布。C++14 并没有像 C++11 那样引入大量的新特性,而是对 C++11 中的一些特性进行了修复、增强和小幅优化。它的目标是提高语言和标准库的稳定性,改进一些 C++11 的边界情况,增强编译器的支持,并提供一些性能上的提升。
主要新特性:
-
泛型 Lambda 表达式:
- C++14 扩展了 C++11 中的 Lambda 表达式,引入了泛型 Lambda,使得 Lambda 可以接受不同类型的参数而不需要显式地指定类型。
-
变量模板:
- C++14 引入了 变量模板,允许创建像模板函数一样的模板变量。之前只有函数和类可以是模板,但 C++14 使得变量也可以具有模板特性。
-
Lambda 表达式的返回类型推导:
- 在 C++14 中,Lambda 表达式可以通过
auto
关键字自动推导返回类型。这避免了在 Lambda 表达式中手动指定返回类型的需要。
- 在 C++14 中,Lambda 表达式可以通过
-
**
std::make_unique
**:- 引入了
std::make_unique
,作为std::make_shared
的对称操作,它简化了对std::unique_ptr
的创建,避免了不必要的new
运算符。
- 引入了
-
**
decltype(auto)
**:- 引入了
decltype(auto)
作为函数返回类型的推导方式,它可以推导出具有精确类型的返回值,支持返回引用类型的自动推导。
- 引入了
-
标准库改进:
- C++14 修复了 C++11 标准库中的一些问题,并改进了对各种常见编译器的支持。标准库中对线程、智能指针、算法等的支持变得更加稳定和高效。
-
二进制字面量:
- C++14 引入了二进制字面量(
0b
或0B
)的支持,使得表示二进制数更加直观。
- C++14 引入了二进制字面量(
-
**
std::exchange
**:- 新增了
std::exchange
函数,它可以简化交换操作,尤其是当你需要交换一个对象并返回其原值时。
- 新增了
示例代码:C++14 新特性
以下代码展示了 C++14 中的一些关键特性,包括泛型 Lambda、变量模板、decltype(auto)
、std::make_unique
等。
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
// 1. 泛型 Lambda 表达式
void genericLambda() {
auto sum = [](auto a, auto b) { return a + b; }; // 泛型 Lambda
std::cout << "Sum of 3 and 4.5: " << sum(3, 4.5) << std::endl;
}
// 2. 变量模板
template <typename T>
T pi = T(3.14159);
void variableTemplate() {
std::cout << "Pi as float: " << pi<float> << std::endl;
std::cout << "Pi as double: " << pi<double> << std::endl;
}
// 3. Lambda 表达式的返回类型推导
void lambdaReturnType() {
auto multiply = [](auto a, auto b) { return a * b; };
std::cout << "Multiply 4 and 5.5: " << multiply(4, 5.5) << std::endl;
}
// 4. 使用 std::make_unique
void makeUnique() {
auto ptr = std::make_unique<int[]>(5); // 使用 make_unique 创建动态数组
ptr[0] = 10;
std::cout << "First element in unique_ptr array: " << ptr[0] << std::endl;
}
// 5. 使用 decltype(auto) 推导返回类型
int&& returnRvalue() {
int x = 5;
return std::move(x); // 返回右值引用
}
void decltypeAuto() {
decltype(auto) result = returnRvalue();
std::cout << "Rvalue returned: " << result << std::endl;
}
// 6. 使用 std::exchange
int increment(int& x) {
return std::exchange(x, x + 1); // 将 x 的当前值赋给函数并更新 x
}
void exchangeExample() {
int value = 10;
int oldValue = increment(value);
std::cout << "Old value: " << oldValue << ", New value: " << value << std::endl;
}
int main() {
// 泛型 Lambda
genericLambda();
// 变量模板
variableTemplate();
// Lambda 返回类型
lambdaReturnType();
// 使用 make_unique
makeUnique();
// 使用 decltype(auto)
decltypeAuto();
// 使用 std::exchange
exchangeExample();
return 0;
}
代码解释:
-
泛型 Lambda 表达式:
- 通过
auto
来声明 Lambda 表达式的参数类型,使得 Lambda 可以接受任意类型的参数。这使得 Lambda 表达式更加灵活,可以应用于多种不同的类型。
- 通过
-
变量模板:
pi
是一个模板变量,它接受一个类型参数T
,并为每个类型提供不同的常量值。在variableTemplate()
中,演示了如何使用模板变量来获取pi
的不同类型值。
-
Lambda 返回类型推导:
- 使用
auto
关键字,Lambda 表达式会自动推导返回类型。在lambdaReturnType()
中,返回类型是乘积结果的类型,auto
会自动推导出其正确类型。
- 使用
-
**
std::make_unique
**:std::make_unique
用于创建std::unique_ptr
,这是一个智能指针,用于管理动态分配的内存。std::make_unique
是 C++14 引入的,它比直接使用new
更加安全和方便。
-
**
decltype(auto)
**:decltype(auto)
用于推导函数的返回类型,并且支持返回值是引用的情况。在decltypeAuto()
中,returnRvalue()
返回一个右值引用,通过decltype(auto)
可以正确地推导出返回类型。
-
**
std::exchange
**:std::exchange
是 C++14 引入的一个小工具,它用于交换一个变量的值并返回旧值。在exchangeExample()
中,std::exchange
用于交换value
的值并返回交换前的值。
运行输出:
Sum of 3 and 4.5: 7.5
Pi as float: 3.14159
Pi as double: 3.14159
Multiply 4 and 5.5: 22
First element in unique_ptr array: 10
Rvalue returned: 5
Old value: 10, New value: 11
C++14 的优势:
- 增强的类型推导:通过
auto
、decltype(auto)
、泛型 Lambda 表达式等新特性,C++14 提供了更加灵活和精简的编程体验。 - 更强大的 Lambda 表达式:C++14 使得 Lambda 表达式支持更复杂的类型推导和捕获方式,提升了函数式编程的能力。
- 内存管理优化:
std::make_unique
提高了内存管理的安全性和效率,避免了直接使用new
时可能出现的资源泄漏问题。 - 简化的交换操作:
std::exchange
使得交换操作更加简洁,尤其是在需要交换并返回原值的场景中。
C++14 的局限性:
- 更新较小:相比 C++11,C++14 的新特性较少,更多的是对 C++11 特性的增强和补充。因此,C++14 的使用场景更多是在已有代码的优化和小修小补上。
- 编译器支持:尽管大部分现代编译器支持 C++14,但早期的编译器可能对一些新特性的支持不完全,开发者在使用这些特性时需要确认编译器的兼容性。
总结:
C++14 是 C++ 语言的一个小版本更新,主要通过增强和扩展 C++11 中的特性来提升语言的灵活性、可用性和安全性。它引入了泛型 Lambda、变量模板、std::make_unique
等重要特性,并修复了 C++11 中的一些边界问题。C++14 对于已经使用 C++11 的开发者来说是一个重要的改进,但它的变化相对较小,不会像 C++11 那样对整个语言体系造成颠覆性的影响。
8. C++ 2017: C++17
- 重要更新:
- 结构化绑定声明:允许通过
auto [x, y] = pair
等语法,直接解构元组或结构体。- **
std::filesystem
**:引入文件系统库,提供跨平台的文件系统操作接口。if
和switch
初始化:允许在if
或switch
语句中直接初始化变量。- 并行算法:STL 中的算法库支持并行执行,如并行排序。
- **
std::optional
**:提供了一个容器,可以包含一个值或为空,用于避免空指针的使用。- **
std::variant
**:实现了类似于联合体的类型,但比 C 的union
更安全和灵活。- **改进的
std::any
**:增强了类型安全的通用容器,允许存储任意类型。
C++17(也被称为C++ 2017)是C++编程语言的一个重要版本,它在C++11和C++14的基础上进行了许多改进,增强了语言特性、库功能以及编译器支持。C++17的主要亮点包括:
1. 结构化绑定声明 (Structured Bindings)
C++17 引入了结构化绑定,允许你将一个结构或元组拆分为多个变量,从而让代码更加简洁易读。
示例:
#include <tuple>
#include <iostream>
std::tuple<int, double, std::string> getData() {
return {1, 3.14, "Hello, C++17!"};
}
int main() {
auto [x, y, z] = getData(); // 结构化绑定
std::cout << x << ", " << y << ", " << z << std::endl;
return 0;
}
输出:
1, 3.14, Hello, C++17!
2. if/else语句中的初始化语句 (If/else with initialization)
C++17允许在if
或else
语句中进行变量初始化,避免了冗余的变量声明。
示例:
#include <iostream>
int main() {
if (int x = 10; x > 5) {
std::cout << "x is greater than 5" << std::endl;
} else {
std::cout << "x is not greater than 5" << std::endl;
}
return 0;
}
输出:
x is greater than 5
注意:x
只在if
或else
的作用域内有效。
3. std::optional
std::optional
是一个模板类,用来表示一个可能为空的值。它可以用来避免使用空指针或特殊值来表示无效数据。
示例:
#include <iostream>
#include <optional>
std::optional<int> findValue(bool found) {
if (found) {
return 42;
} else {
return std::nullopt; // 返回空值
}
}
int main() {
auto value = findValue(true);
if (value) {
std::cout << "Found value: " << *value << std::endl;
} else {
std::cout << "Value not found" << std::endl;
}
return 0;
}
输出:
Found value: 42
4. std::string_view
std::string_view
是一个轻量级的字符串视图,避免了复制字符串数据,提供了对字符串的只读访问。
示例:
#include <iostream>
#include <string_view>
void printString(std::string_view str) {
std::cout << str << std::endl;
}
int main() {
std::string str = "Hello, C++17!";
printString(str); // 不会复制数据
return 0;
}
输出:
Hello, C++17!
5. std::filesystem
C++17加入了对文件系统操作的标准库支持,std::filesystem
使得处理文件和目录变得更加方便。
示例:
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path p = "example.txt";
if (fs::exists(p)) {
std::cout << p << " exists!" << std::endl;
} else {
std::cout << p << " does not exist." << std::endl;
}
return 0;
}
输出:
example.txt does not exist.
6. 并行算法 (Parallel Algorithms)
C++17添加了对并行执行算法的支持,例如使用 std::for_each
和其他标准算法的并行执行。
示例:
#include <iostream>
#include <vector>
#include <algorithm>
#include <execution>
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::for_each(std::execution::par, data.begin(), data.end(), [](int& x) {
x *= 2;
});
for (int x : data) {
std::cout << x << " ";
}
return 0;
}
输出:
2 4 6 8 10 12 14 16 18
(注意:并行执行可能因环境不同而产生不同的输出顺序)
7. 内联变量 (Inline Variables)
C++17引入了内联变量,解决了头文件中静态变量带来的链接问题。
示例:
#include <iostream>
inline int x = 42; // 内联变量
int main() {
std::cout << x << std::endl;
return 0;
}
8. 改进的constexpr
C++17扩展了constexpr
的功能,允许在编译时执行更多的操作,比如动态分配内存和使用if
语句。
示例:
#include <iostream>
constexpr int factorial(int n) {
return (n == 0) ? 1 : n * factorial(n - 1);
}
int main() {
std::cout << "Factorial of 5: " << factorial(5) << std::endl;
return 0;
}
输出:
Factorial of 5: 120
总结
C++17引入了许多新特性,极大提高了语言的表达力和编程效率,尤其是在简化代码、提高性能和增加灵活性方面。对于现代C++开发者来说,熟练掌握这些特性将大大提升开发体验和代码质量。
9. C++ 2020: C++20
- 重大的新特性:
- 模块(Modules):引入了模块化的支持,替代传统的头文件和源文件方式,减少编译时间。
- 协程(Coroutines):支持异步编程,简化了异步函数的编写。
- 概念(Concepts):引入了模板约束,使得模板编程更加灵活和易于理解。
- 范围(Ranges):增强了容器操作的灵活性和简洁性,提供了更强大的算法支持。
- **
std::format
**:提供了类似 Python 中的格式化字符串功能,简化了输出。- **
consteval
和constinit
**:新关键字,用于常量表达式和常量初始化的严格控制。- **改进的
constexpr
**:支持更多的编译时功能,包括动态内存分配。
C++20 是 C++ 标准的一个重要更新,它在语言特性、库功能、并发支持等方面进行了大量的扩展和增强。C++20 对语言进行了重要的现代化,使得 C++ 更加高效、简洁、易用。以下是 C++20 中的一些主要特性及其代码示例:
1. 概念 (Concepts)
概念是 C++20 的一项新特性,用来约束模板类型。它允许我们更精确地控制模板参数类型,提供编译时检查和更好的错误信息。
示例:
#include <iostream>
#include <concepts>
template <typename T>
concept Incrementable = requires(T a) { ++a; a++; };
template <Incrementable T>
T increment(T x) {
return ++x;
}
int main() {
int a = 5;
std::cout << increment(a) << std::endl; // 正常工作
// std::string s = "Hello";
// std::cout << increment(s) << std::endl; // 编译错误,因为 std::string 不符合 Incrementable 概念
return 0;
}
输出:
6
2. 范围 (Ranges)
C++20 引入了 std::ranges
,为标准库中的容器和算法提供了更加一致和方便的操作方法,支持通过管道操作符(|
)组合算法。
示例:
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};
// 使用管道操作符对容器进行操作
auto result = nums | std::views::transform([](int x) { return x * 2; })
| std::views::filter([](int x) { return x > 5; });
for (int val : result) {
std::cout << val << " ";
}
return 0;
}
输出:
6 8 10
3. 协程 (Coroutines)
C++20 引入了协程支持,使得编写异步代码更加简洁。协程允许暂停和恢复函数的执行,适用于处理 I/O 操作或其他需要异步操作的场景。
示例:
#include <iostream>
#include <coroutine>
struct Task {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
Task get_return_object() { return Task{handle_type::from_promise(*this)}; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::exit(1); }
void return_void() {}
};
handle_type coro;
Task(handle_type h) : coro(h) {}
~Task() { coro.destroy(); }
void resume() { coro.resume(); }
};
Task simpleCoroutine() {
std::cout << "Start of coroutine\n";
co_await std::suspend_always{};
std::cout << "End of coroutine\n";
}
int main() {
auto coro = simpleCoroutine();
coro.resume(); // 启动协程
coro.resume(); // 恢复协程
return 0;
}
输出:
Start of coroutine
End of coroutine
4. 三向比较 (Spaceship Operator <=>
)
C++20 引入了“太空船操作符” (<=>
),也叫做三向比较操作符,它提供了一种简化的方式来执行比较操作,如 <
、<=
、==
、>
、>=
。
示例:
#include <iostream>
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // 默认生成比较操作符
};
int main() {
Point p1 = {1, 2}, p2 = {2, 3};
if (p1 < p2) {
std::cout << "p1 is less than p2\n";
}
return 0;
}
输出:
p1 is less than p2
5. 模块 (Modules)
C++20 引入了模块的概念,这是对头文件的替代。模块可以提高编译速度,避免头文件带来的重复处理和依赖问题。模块支持 import
语句来替代传统的 #include
。
示例:
假设我们有一个模块 math
:
// math.cppm - 模块定义文件
export module math;
export int add(int a, int b) { return a + b; }
然后在主程序中导入该模块:
// main.cpp
import math;
int main() {
int result = add(3, 4);
std::cout << result << std::endl; // 输出 7
return 0;
}
注意:模块在许多编译器中仍处于实验阶段,需要启用特定的编译器选项。
6. 范围循环 (Range-based for loop with initializer)
C++20 增强了范围 for 循环,允许在循环中初始化变量。
示例:
#include <iostream>
#include <vector>
int main() {
std::vector<int> nums = {1, 2, 3, 4};
for (auto&& [index, value] : nums) { // 使用结构化绑定
std::cout << "Value: " << value << std::endl;
}
return 0;
}
输出:
Value: 1
Value: 2
Value: 3
Value: 4
7. std::span
std::span
是 C++20 新增的一个轻量级的视图容器,它可以用来表示一个数组的连续区间。它与传统的指针数组相比,更加安全,并支持对数组进行边界检查。
示例:
#include <iostream>
#include <span>
void printSpan(std::span<int> sp) {
for (auto val : sp) {
std::cout << val << " ";
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
printSpan(arr); // 使用 std::span 传递数组
return 0;
}
输出:
1 2 3 4 5
8. consteval
和 constinit
C++20 引入了 consteval
和 constinit
关键字:
consteval
用于指定一个函数必须在编译时求值。constinit
用于确保某个变量在初始化时是常量。
示例:
#include <iostream>
consteval int square(int n) { return n * n; }
int main() {
constexpr int result = square(5); // 在编译时计算
std::cout << result << std::endl; // 输出 25
return 0;
}
输出:
25
总结
C++20 引入了许多强大的新特性,极大增强了 C++ 的表达能力和编程效率。它使得编写更现代、更安全和更高效的 C++ 代码变得更加容易。例如,概念和协程提高了代码的可读性和可维护性,而模块的引入可以显著提高编译性能。掌握 C++20 的特性将有助于编写更清晰、强大和高效的 C++ 代码。
10. C++ 2023: C++23
- 新特性和小幅增强:
- **扩展的
std::ranges
**:提供更多的支持与增强,使得 C++ 的标准库更具表达力和功能。
- 改进的协程支持:协程功能得到进一步加强,增加了更多的灵活性和优化。
- 更好的标准化和规范:提高了 C++ 语言和标准库的易用性和性能。
C++23(正式名称为 C++23)是C++语言的最新标准,作为C++20和C++17之后的下一代标准,C++23在语言特性、库功能和编译器优化等方面引入了一些重要的改进。以下是C++23中的一些新特性和代码示例。
1. 改进的常量表达式(constexpr
)
C++23扩展了constexpr
的功能,允许更多复杂的功能在编译时执行。例如,C++23允许在constexpr
函数中使用try-catch
语句,这在之前的标准中是无法实现的。
示例:
constexpr int safe_divide(int a, int b) {
if (b == 0) throw std::invalid_argument("Division by zero");
return a / b;
}
int main() {
constexpr int result = safe_divide(10, 2); // 编译时执行
return 0;
}
2. std::expected
C++23引入了std::expected
,它是一个可以用来表示函数调用结果的类型,类似于std::optional
,但是它包含了错误信息。这个特性非常适合替代try-catch
块来进行错误处理,尤其是在函数返回时希望包含更多的错误信息时。
示例:
#include <expected>
#include <iostream>
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}
int main() {
auto result = divide(10, 0);
if (result) {
std::cout << "Result: " << *result << std::endl;
} else {
std::cout << "Error: " << result.error() << std::endl;
}
return 0;
}
3. 范围(Ranges)库增强
C++20引入了范围(Ranges)库,C++23在此基础上进行了增强。新增了更丰富的算法和视图,使得在进行集合操作时,代码更加简洁且易于理解。
示例:
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 使用ranges库处理集合
auto result = v | std::views::transform([](int n) { return n * n; })
| std::views::filter([](int n) { return n > 10; });
for (int i : result) {
std::cout << i << " ";
}
return 0;
}
4. std::format
改进
C++20引入了 std::format
来格式化字符串,C++23对其进行了进一步改进。新增了更多的格式化选项,以及对各种类型的支持。
示例:
#include <format>
#include <iostream>
int main() {
int age = 25;
std::string name = "Alice";
std::string formatted = std::format("Name: {}, Age: {}", name, age);
std::cout << formatted << std::endl;
return 0;
}
5. explicit
改进
C++23扩展了explicit
关键字,可以用于更加灵活的构造函数,使其适应更复杂的类型转换需求。
示例:
struct A {
explicit A(int x) {} // 显式构造函数
};
struct B {
explicit B(A a) {} // 显式构造函数
};
int main() {
A a(1);
B b(a); // 需要显示构造A对象的参数
return 0;
}
6. std::span
支持的改进
std::span
是C++20中引入的用于表示数组片段的类,C++23对其进行了扩展,支持了更多的功能。
示例:
#include <span>
#include <iostream>
void print_span(std::span<int> s) {
for (auto elem : s) {
std::cout << elem << " ";
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
std::span<int> sp(arr, 3); // 只传递数组的前3个元素
print_span(sp);
return 0;
}
7. 新的属性([[likely]]
和 [[unlikely]]
)
C++23引入了 [[likely]]
和 [[unlikely]]
属性,用于给编译器提供分支预测的提示,帮助优化代码。
示例:
#include <iostream>
int main() {
bool condition = true;
if ([[likely]] condition) {
std::cout << "Condition is likely true" << std::endl;
} else {
std::cout << "Condition is unlikely true" << std::endl;
}
return 0;
}
总结
C++23增强了语言的表达能力和效率,提供了更多的编程便利性,尤其在常量表达式、错误处理、范围操作、字符串格式化和编译时优化方面有显著的提升。这些特性使得C++更加现代化,代码更加简洁易懂,同时也带来了更高效的执行性能。
这些新特性和API增强为开发者提供了更加灵活和强大的工具,可以让代码更加高效、易维护并且更具可读性。