C++11,正式名称为 ISO/IEC 14882:2011,于 2011 年发布,是 C++ 编程语言演变过程中的一个重要里程碑。
它引入了大量的全新功能和增强功能,使 C++ 更加强大。
C++ 11 引入的功能
以下是 C++ 11 引入的最重要的功能:
auto
关键字- 基于范围的
for
循环 - Lambda 表达式
- 智能指针
constexpr
关键字nullptr
关键字- 类型特性
- 线程支持
- 委托构造函数
- 已删除和已默认的函数
让我们从探索 auto
关键字开始。
C++ auto 关键字
auto
关键字允许 C++11 从初始化器自动推导变量的类型。
#include <iostream>
using namespace std;
int main() {
// x is deduced as int
auto x = 42;
// pi is deduced as double
auto pi = 3.1415926535;
cout << "x: " << x << endl;
cout << "pi: " << pi << endl;
return 0;
}
输出
x: 42 pi: 3.14159
这里,编译器能够从其初始化器推导出变量 x 和 pi 的类型,因此我们无需显式指定它们的类型。但是,在使用 auto
时,变量必须始终进行初始化。
auto x; // error: must be always initialized
注意:auto
是 C++ 中的类型占位符,而不是类型本身。因此,它不能用于强制转换或类似 sizeof
或 typeid
的运算符。
基于范围的 for 循环
C++ 中的基于范围的 for 循环会为范围执行循环。
语法
for (range_initialization : range_container) {
// loop statements
}
这里,
- range_initialization - 创建一个迭代变量。
- range_container - 要迭代的容器。
例如,
vector<int> numbers = {1, 2, 3};
// loop through the numbers vector
for (int num : numbers) {
// print vector element
cout << num << endl;
}
注意:应尽可能优先使用基于范围的 for 循环,而不是普通的for 循环,以避免越界错误。
示例 1:C++ 基于范围的 for 循环
#include <iostream>
#include <vector>
using namespace std;
int main() {
// create a vector of integers
vector<int> numbers = {1, 2, 3, 4, 5};
// use a range-based for loop
// to iterate through the vector
for (const auto& num : numbers) {
cout << num << " ";
}
return 0;
}
输出
1 2 3 4 5
在这里,我们声明了迭代变量 num,并使用基于范围的 for 循环迭代了容器 numbers。
Lambda 表达式
Lambda 表达式允许您以简洁的方式创建匿名函数。当您需要将小型函数作为参数传递给其他函数时,它们特别有用。
语法
[capture_clause] (parameter_list) -> return_type {
// lambda body: Code to be executed
}
这里,
- capture_clause - 指定 lambda 函数中可访问的周围作用域中的哪些变量。
- parameter_list - 定义函数接受的参数。
- -> return_type - 声明函数的返回类型。
注意:除非存在多个不同类型的可能返回值,否则我们可以省略 return_type 以实现自动类型推导。
要了解更多信息,请访问我们的C++ Lambda 教程。
示例 2:C++ Lambda 表达式
#include <iostream>
using namespace std;
int main() {
// define a lambda function named 'add'
// that takes two integers and returns their sum
auto add = [] (int a, int b) {
return a + b;
};
// call the lambda with arguments 3 and 4
int result = add(3, 4);
// print the result.
cout << "Result: " << result << endl;
return 0;
}
输出
Result: 7
在这里,我们创建了一个 lambda 函数 add(),它接受两个参数 a 和 b 并返回它们的和。
智能指针
C++11 引入了智能指针,它们可以自动管理内存并有助于防止内存泄漏。基本上,智能指针会在其作用域结束时自动释放其管理的内存。
智能指针有两种类型:
- 独占指针(Unique Pointers)独占其指向的对象。
- 共享指针(Shared Pointers)允许多个共享指针拥有单个对象。
语法
// unique pointer
std::unique_ptr<data_type> unique_pointer = std::make_unique<data_type>(args...);
// shared pointer
std::shared_ptr<Type> shared_pointer = std::make_shared<data_type>(args...);
注意:我们需要包含 <memory>
头文件才能使用智能指针。
示例 3:C++ 智能指针
#include <iostream>
#include <memory>
using namespace std;
int main() {
// create a shared pointer to an integer with value 42
shared_ptr<int> shared_pointer = make_shared<int>(42);
// create a unique pointer to a double with value 3.14
unique_ptr<double> unique_pointer = make_unique<double>(3.14);
// print the value pointed to by shared_ptr
cout << "shared_pointer: " << *shared_pointer << endl;
// print the value pointed to by unique_ptr
cout << "unique_pointer: " << *unique_pointer << endl;
return 0;
}
输出
shared_pointer: 42 unique_pointer: 3.14
移动语义
移动语义允许将一个对象拥有的资源移动到另一个对象,而不是复制它们。这通过避免深拷贝来优化性能。
我们使用 move()
函数来实现移动语义。例如,
#include <iostream>
#include <vector>
using namespace std;
int main() {
// create an integer vector
vector<int> source = {1, 2, 3};
// move the contents of source to another vector
vector<int> destination = move(source);
// print the destination vector
cout << "Destination Vector Contents: ";
for (const int num : destination) {
cout << num << " ";
}
// print the size of the destination vector
cout << "\nDestination Vector Size: " << destination.size();
return 0;
}
输出
Destination Vector Contents: 1 2 3 Destination Vector Size: 3
在这里,我们使用 move()
函数将 source 的内容移动到 destination。
vector<int> destination = move(source);
C++ constexpr 关键字
constexpr
关键字允许您指定变量或函数可以在编译时求值。例如,
constexpr int square(int x) {
return x * x;
}
// computed at compile-time
int result = square(5);
在这里,我们将 constexpr
关键字与 square()
函数一起使用。因此,square(5)
可以在编译时而不是运行时求值。
这可以提高代码性能,并确保表达式使用在编译时已知的的值进行初始化。
空指针
引入 nullptr
为使用 NULL
作为空指针提供了一个更安全的选择。
int* ptr = nullptr;
注意:由于 nullptr
比 NULL
更安全,请始终优先使用 nullptr
而不是 NULL
。
类型特性
在 C++ 中,类型特性是一组模板,用于在编译时收集有关类型的信息,是模板元编程的强大工具。
这意味着在程序运行之前,编译器就能了解您正在使用的类型——例如,类型是否为整数,是否可以复制,或者它是否是一个具有特定函数的类。
它们是标准模板库(STL)的一部分,包含在 <type_traits>
头文件中。
类型特性是使用类模板或函数模板实现的。下面是一个函数模板的示例代码。
template <typename T>
void process(T value) {
if (std::is_pointer<T>::value) {
// handle pointers
}
else if (std::is_integral<T>::value) {
// handle integers
}
}
线程支持
C++11 添加了一个标准化的线程库,允许您创建和管理线程。
#include <iostream>
#include <thread>
using namespace std;
void sayHello(){
cout << "Hello, from the spawned thread\n";
}
int main(){
// create a std::thread object
std::thread t(sayHello);
std::cout << "Hello, from the main thread\n";
// wait for the thread to complete its job
t.join();
}
输出
Hello, from the main thread Hello, from the spawned thread
注意:输出是执行依赖的,因此您每次运行它时可能不会得到相同的输出。
每个线程都必须有一个初始函数,新线程的执行从那里开始。
因此,我们通过传递我们希望线程执行的函数 sayHello 来创建了一个 std::thread
对象 t。
现在程序有两个线程——一个执行 main 函数,另一个执行 sayHello
函数。
委托构造函数
在 C++11 中,一个构造函数可以调用同一类中的另一个构造函数。例如,
#include <iostream>
using namespace std;
class Complex {
int img;
int real;
public:
// constructor #1 (target constructor)
Complex(int i, int r) : img(i), real(r) {}
// constructor #2 (delegating constructor)
// pass two zeroes as arguments to target constructor
Complex() : Complex(0, 0) {
cout << "Delegating constructor" << endl;
cout << "img = " << img << endl;
cout << "real = " << real << endl;
}
};
int main() {
// create an instance of class Complex
// using the delegating constructor
Complex obj;
return 0;
}
输出
Delegating constructor img = 0 real = 0
在此,委托构造函数调用了目标构造函数。
已删除和已默认的函数
1. 已删除函数
它们是已被故意标记为已删除的成员函数。这意味着这些函数不能被使用,任何尝试调用它们的行为都将导致编译错误。
删除函数对于防止对类的某些操作很有用。
class My_Class {
public:
// delete the default constructor
My_Class() = delete;
// delete a member function
void do_something() = delete;
};
2. 已默认的函数
它们用于显式请求编译器为某些成员函数生成默认实现。
当您希望利用编译器生成的版本,同时为其他函数提供自定义行为时,这特别有用。
class Another_Class {
public:
// use the compiler-generated default constructor
Another_Class() = default;
// define a custom constructor
Another_Class(int x) : value(x) {}
private:
int value;
};
示例 4:C++ 已删除和已默认的函数
class Example {
public:
// defaulted default constructor
Example() = default;
// deleted copy constructor
Example(const Example&) = delete;
};
int main() {
// allowed: default constructor is used
Example obj1;
// error: copy constructor is deleted
// Example obj2 = obj1;
return 0;
}
这里,
- 我们可以使用默认构造函数创建
Example
类的一个对象。 - 我们不能创建
Example
类对象的副本,因为复制构造函数已被删除。