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年 |
auto
);&&
、std::move
);std::thread
);std::unique_ptr
、std::shared_ptr
);decltype
)等。std::make_unique
的引入、decltype(auto)
的改进等。此外,还修复了一些编译器和实现中的问题。std::optional
**:用于表示可能为空的值;auto [a, b] = tuple;
);if
和 switch
中的初始化语句;std::filesystem
**:用于处理文件和目录的标准库;std::string
和 std::array
**;std::for_each
等支持并行执行)。<=>
): 用于自动生成比较操作符(比如 ==
, !=
, <
, <=
, >
, >=
);std::span
**:处理数组和内存块的轻量级对象;std::format
、std::ranges
、std::calendar
等。std::expected
、std::generator
和 std::ranges
的进一步改进;C++ 的发展历程体现了它强大的灵活性和持久生命力,C++ 不断演进,始终保持着与时俱进的能力,成为全球使用最广泛的编程语言之一。
C++ 的发展历程可以分为多个版本阶段,每个版本都带来了新的语言特性、库的改进和标准的增强。下面是 C++ 各个版本的主要变化和特性介绍:
C++ 1.0 是由 Bjarne Stroustrup 在贝尔实验室开发的第一版 C++ 编程语言。它是在 C 语言的基础上加入了面向对象编程(OOP)的特性,因此也被称为是“C with Classes”(C 带类)。C++ 1.0 并不是一个标准化的语言版本,而是一个实验性语言,它为后来的 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 语言的功能。虽然当时的 C++ 功能较为简单,但它为后续版本的语言特性打下了基础,并引领了 C++ 向更强大的方向发展。
C++ 2.0 是 C++ 语言的第二个重要版本,发布于 1989 年。C++ 2.0 在 C++ 1.0 的基础上进行了重要的扩展和改进,引入了多个新特性,特别是 多重继承 和 虚继承。这些新特性使得 C++ 的面向对象编程能力得到了显著增强。C++ 2.0 为后来的 C++ 语言标准奠定了许多重要基础。
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
有一个 showA()
函数,B
有一个 showB()
函数。C
类继承了 A
类,并使用 virtual
关键字进行 虚继承。这样,在 C
的子类中,如果有多个继承自 A
的路径,它们会共享同一个 A
类副本,避免了重复数据。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++ 的面向对象能力,使得 C++ 成为一种更强大的编程语言。这些特性让 C++ 能够支持更复杂的类层次结构,解决了多重继承中的一些难题。虽然 C++ 2.0 语言仍然很基础,但它为 C++ 后续版本的更高级特性,如模板和更复杂的类型系统,奠定了基础。
C++ 3.0 是 C++ 语言的第三个版本,发布于 1990 年。相比于前两个版本,C++ 3.0 在语言特性和功能上做出了重要的扩展,最为关键的就是引入了 模板 的支持,这是 C++ 语言向 泛型编程 过渡的重要一步。模板的引入使得 C++ 支持编写与类型无关的代码,从而使得编程变得更加灵活和强大。
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++ 语言的一个重要版本,模板的引入是其最大的亮点,标志着 C++ 进入了泛型编程的时代。模板让 C++ 变得更加灵活,支持更加高效的代码重用和类型安全的操作。通过函数模板和类模板,C++ 可以在不同类型之间共享代码,从而极大地提高了开发效率和代码的可维护性。虽然 C++ 3.0 的模板机制相对简单,但它为后续的 C++ 标准化进程和更复杂的模板特性奠定了基础。
vector
、list
、map
、set
等容器,及算法(如排序、查找等)。map - C++ Reference set - C++ Referencetry
、catch
、throw
异常处理机制。typeid
和 dynamic_cast
,允许运行时识别对象的类型。C++98 是 C++ 语言的第一个国际标准,正式由 ISO(国际标准化组织)在 1998 年发布。C++98 语言标准继承了 C++ 3.0 的核心特性,并在此基础上进行了多个重要的改进和规范化。这个版本的 C++ 为标准库(STL)提供了规范,并开始支持更多的现代编程特性。
try
, catch
, throw
语句来处理运行时错误。namespace
),用于避免名称冲突,尤其是在大型项目中。const
关键字和引用进行了严格的规范,使得编程更加安全。static_cast
, dynamic_cast
等)的支持,增强了 C++ 的灵活性。以下是一个综合示例,展示了 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
是占位符类型,可以在调用时由编译器自动推导出。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
。std::vector
和 std::sort
**:std::vector
是一个可以动态调整大小的容器,它存储一系列元素。std::sort
是一个通用算法,用来对容器中的元素进行排序。auto
关键字:C++98 中没有 auto
关键字,因此类型推导只能依靠显式的类型声明。nullptr
**:C++98 中没有 nullptr
,指针使用时可能会引发隐式的类型转换问题。for
循环:C++98 中没有像 C++11 中那样的基于范围的 for
循环(for (auto& elem : container)
)。C++98 是 C++ 语言的第一个国际标准版本,它巩固并标准化了之前 C++3.0 中的许多特性,并加入了 模板 和 STL 等重要特性,使得 C++ 在实际开发中更加高效和灵活。通过模板,C++98 使得代码可以在不同的数据类型之间重用,同时标准库(STL)提供了高效的容器和算法支持,大大提高了开发效率。虽然 C++98 为后来的 C++ 标准奠定了基础,但它在某些领域(如模板编程和自动类型推导)仍存在局限。
C++03 是 C++ 语言的一个小版本更新,它是对 C++98 的修正版本,主要用于解决一些语言上的不一致性和设计上的小问题。C++03 并未引入许多新的功能,而是对 C++98 标准中的一些细节进行了修正和澄清,确保语言的一致性和规范性。这个版本主要集中在语言规范的完善和一些编译器支持方面。
C++03 的发布并没有像 C++98 到 C++11 之间的版本那样进行革命性的更新,因此它在功能上与 C++98 并无太大差异。主要的更新包括对 模板特化、SFINAE(Substitution Failure Is Not An Error)等方面的细节修正,确保了编译器能够更好地支持标准库。
std::vector
在某些情况下的行为不一致性,确保了 STL 中的算法与容器的更好兼容性。由于 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 中明确规定的,确保了特化行为的统一性。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
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 更多的是一种稳定性和兼容性更新,而不是一种功能性扩展。
auto
):使用 auto
关键字可以让编译器推断变量类型。std::unique_ptr
、std::shared_ptr
、std::weak_ptr
,用于自动管理内存,避免内存泄漏。<thread>
库,允许多线程编程。nullptr
**:引入了 nullptr
关键字,替代 NULL
,提高代码的类型安全。&&
)和 std::move
,提高对象传递效率,减少不必要的拷贝。for
循环,简化容器遍历。using
):引入 using
关键字,简化类型别名定义。C++11 是 C++ 语言的一个重大更新,正式于 2011 年发布。它引入了大量新的特性和改进,使 C++ 语言更现代化、更加高效和易于使用。C++11 标准的推出,极大地扩展了 C++ 的功能,使得 C++ 开发变得更加灵活、简洁,同时还提升了性能。
auto
):
auto
关键字允许编译器根据表达式的类型自动推导变量的类型,减少了类型声明的冗余,提高了代码可读性。&&
和 std::move
):
&&
)和 std::move
,实现了 移动语义。通过移动资源而不是复制,显著提高了效率,尤其在需要大量数据传输的场合(如容器操作)。for
循环:
for
循环(range-based for loop),简化了容器元素的遍历,避免了传统迭代器的复杂性。nullptr
**:
nullptr
替代了 NULL
,它是一个类型安全的空指针常量,消除了 NULL
的类型不匹配问题。constexpr
**:
constexpr
使得可以在编译时求值的常量表达式更加简洁,增强了编译期计算的能力。<thread>
库:
<thread>
库使得多线程编程变得更加容易和高效。using
):
using
关键字可以用来为类型定义别名,取代了传统的 typedef
,语法更加简洁。enum class
):
enum class
,避免了传统枚举可能产生的隐式转换和命名冲突问题。std::chrono
**:
std::chrono
,使得处理时间变得更加直观和精确。下面的代码示例展示了 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
循环:
for
循环来遍历容器。这个语法简洁、直观,避免了显式的迭代器操作。std::for_each
算法与 lambda 表达式结合,计算容器中所有元素的总和。lambda 表达式 [&sum](int num) { sum += num; }
捕获外部变量 sum
并对容器中的每个元素执行加法操作。moveExample
中,通过 std::move
实现了对象 a
的移动,将其资源转移给对象 b
,触发了移动构造函数而不是拷贝构造函数。<thread>
库创建和启动一个线程,并且通过 join()
等待线程执行完成。std::thread
简化了多线程编程。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++ 语言的一次重大更新,带来了许多新的语言特性和库支持,使得 C++ 编程变得更加简洁、强大和高效。无论是在性能优化、现代语法的引入,还是在并发支持等方面,C++11 都大幅提升了语言的可用性和表达能力。
constexpr
的改进:支持更复杂的编译时常量计算。auto
的增强:使得 auto
可以推导更加复杂的类型。*this
引用)。C++14 是 C++ 语言的一个相对较小的更新版本,于 2014 年发布。C++14 并没有像 C++11 那样引入大量的新特性,而是对 C++11 中的一些特性进行了修复、增强和小幅优化。它的目标是提高语言和标准库的稳定性,改进一些 C++11 的边界情况,增强编译器的支持,并提供一些性能上的提升。
auto
关键字自动推导返回类型。这避免了在 Lambda 表达式中手动指定返回类型的需要。std::make_unique
**:
std::make_unique
,作为 std::make_shared
的对称操作,它简化了对 std::unique_ptr
的创建,避免了不必要的 new
运算符。decltype(auto)
**:
decltype(auto)
作为函数返回类型的推导方式,它可以推导出具有精确类型的返回值,支持返回引用类型的自动推导。0b
或 0B
)的支持,使得表示二进制数更加直观。std::exchange
**:
std::exchange
函数,它可以简化交换操作,尤其是当你需要交换一个对象并返回其原值时。以下代码展示了 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;
}
auto
来声明 Lambda 表达式的参数类型,使得 Lambda 可以接受任意类型的参数。这使得 Lambda 表达式更加灵活,可以应用于多种不同的类型。pi
是一个模板变量,它接受一个类型参数 T
,并为每个类型提供不同的常量值。在 variableTemplate()
中,演示了如何使用模板变量来获取 pi
的不同类型值。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
auto
、decltype(auto)
、泛型 Lambda 表达式等新特性,C++14 提供了更加灵活和精简的编程体验。std::make_unique
提高了内存管理的安全性和效率,避免了直接使用 new
时可能出现的资源泄漏问题。std::exchange
使得交换操作更加简洁,尤其是在需要交换并返回原值的场景中。C++14 是 C++ 语言的一个小版本更新,主要通过增强和扩展 C++11 中的特性来提升语言的灵活性、可用性和安全性。它引入了泛型 Lambda、变量模板、std::make_unique
等重要特性,并修复了 C++11 中的一些边界问题。C++14 对于已经使用 C++11 的开发者来说是一个重要的改进,但它的变化相对较小,不会像 C++11 那样对整个语言体系造成颠覆性的影响。
auto [x, y] = pair
等语法,直接解构元组或结构体。std::filesystem
**:引入文件系统库,提供跨平台的文件系统操作接口。if
和 switch
初始化:允许在 if
或 switch
语句中直接初始化变量。std::optional
**:提供了一个容器,可以包含一个值或为空,用于避免空指针的使用。std::variant
**:实现了类似于联合体的类型,但比 C 的 union
更安全和灵活。std::any
**:增强了类型安全的通用容器,允许存储任意类型。C++17(也被称为C++ 2017)是C++编程语言的一个重要版本,它在C++11和C++14的基础上进行了许多改进,增强了语言特性、库功能以及编译器支持。C++17的主要亮点包括:
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!
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
的作用域内有效。
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
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!
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.
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
(注意:并行执行可能因环境不同而产生不同的输出顺序)
C++17引入了内联变量,解决了头文件中静态变量带来的链接问题。
#include <iostream>
inline int x = 42; // 内联变量
int main() {
std::cout << x << std::endl;
return 0;
}
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++开发者来说,熟练掌握这些特性将大大提升开发体验和代码质量。
std::format
**:提供了类似 Python 中的格式化字符串功能,简化了输出。consteval
和 constinit
**:新关键字,用于常量表达式和常量初始化的严格控制。constexpr
**:支持更多的编译时功能,包括动态内存分配。C++20 是 C++ 标准的一个重要更新,它在语言特性、库功能、并发支持等方面进行了大量的扩展和增强。C++20 对语言进行了重要的现代化,使得 C++ 更加高效、简洁、易用。以下是 C++20 中的一些主要特性及其代码示例:
概念是 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
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
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
<=>
)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
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;
}
注意:模块在许多编译器中仍处于实验阶段,需要启用特定的编译器选项。
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
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
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++ 代码。
std::ranges
**:提供更多的支持与增强,使得 C++ 的标准库更具表达力和功能。 C++23(正式名称为 C++23)是C++语言的最新标准,作为C++20和C++17之后的下一代标准,C++23在语言特性、库功能和编译器优化等方面引入了一些重要的改进。以下是C++23中的一些新特性和代码示例。
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
[[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增强为开发者提供了更加灵活和强大的工具,可以让代码更加高效、易维护并且更具可读性。