C++ 11

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

这里,编译器能够从其初始化器推导出变量 xpi 的类型,因此我们无需显式指定它们的类型。但是,在使用 auto 时,变量必须始终进行初始化。

auto x; // error: must be always initialized

注意auto 是 C++ 中的类型占位符,而不是类型本身。因此,它不能用于强制转换或类似 sizeoftypeid 的运算符。


基于范围的 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(),它接受两个参数 ab 并返回它们的和。


智能指针

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;

注意:由于 nullptrNULL 更安全,请始终优先使用 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 类对象的副本,因为复制构造函数已被删除。
你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战