【C++】C++11特性

这篇具有很好参考价值的文章主要介绍了【C++】C++11特性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++】C++11特性

1、列表初始化

1.1 {}初始化

C++11引入了初始化列表(Initializer lists)的特性,通过花括号{}可以方便地初始化数组、容器和自定义类型的成员变量。这种语法可以一次性地指定多个初始值,而不需要显式地编写多个赋值语句。

下面是一些示例用法:

  1. 初始化数组:
int arr[] = {1, 2, 3, 4, 5};
  1. 初始化容器(如std::vector):
std::vector<int> vec = {10, 20, 30, 40, 50};
  1. 初始化自定义类型的成员变量:
class Point {
public:
  int x;
  int y;
};

Point p = {10, 20};

使用初始化列表的语法可以更直观地初始化多个值,并且在某些情况下可以提高代码的可读性和效率。

1.2 std::initializer_list

std::initializer_list是C++11引入的一个模板类,用于简化和处理初始化列表。它定义在头文件<initializer_list>中,并提供了一种方便的方式来传递和操作初始化列表。

std::initializer_list允许以花括号{}中的值序列作为参数传递给函数或构造函数。它的语法类似于标准容器,但不同于容器,std::initializer_list本身并不拥有元素,而只是提供了对初始化列表中元素的访问。

下面是一个简单的示例,展示了如何使用std::initializer_list

#include <initializer_list>
#include <iostream>

void printValues(std::initializer_list<int> values) {
  for (auto it = values.begin(); it != values.end(); ++it) {
    std::cout << *it << " ";
  }
  std::cout << std::endl;
}

int main() {
  printValues({1, 2, 3, 4, 5});
  
  return 0;
}

在上面的例子中,printValues函数接受一个std::initializer_list<int>类型的参数,然后遍历并打印出其中的值。在main函数中,我们使用花括号创建一个初始化列表,并将其作为参数传递给printValues函数。

std::initializer_list还可以用于自定义类型的构造函数,使得自定义类型可以方便地接受和处理初始化列表作为参数。

需要注意的是,std::initializer_list中的元素是常量,因此不能修改其中的值。如果需要修改元素,可以将其拷贝到其他容器或数据结构中。

std::initializer_list使用场景:

std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator= 的参数,这样就可以用大括号赋值。

2、声明

2.1 auto

在C++11中,auto是一个关键字,用于进行自动类型推导。使用auto关键字可以使编译器根据变量的初始化表达式来自动推导出变量的类型,而无需显式地指定类型。

下面是一个示例,展示了auto的使用:

auto num = 42;  // 自动推导为 int
auto name = "John";  // 自动推导为 const char*
auto result = computeResult();  // 自动推导为函数返回类型

在上述示例中,变量num的类型被自动推导为int,变量name的类型被自动推导为const char*,而变量result的类型被自动推导为某个函数的返回类型。

auto的使用可以简化代码,特别是当变量的类型较为复杂或难以表达时,可以避免手动编写冗长的类型声明。此外,它还可以与其他C++11特性(如范围for循环和Lambda表达式)结合使用,提供更简洁和灵活的编程方式。

需要注意的是,auto并不是一种动态类型或者弱类型,它只是让编译器根据上下文推导出变量的类型。推导得到的类型在编译时就是确定的,不会在运行时发生类型的变化。

另外,C++14引入了decltype(auto)的语法,结合了autodecltype的特性,可以更精确地进行类型推导。这个特性允许在推导类型时保留变量的引用性质(左值或右值引用)或常量性质。

2.2 decltype

在C++中,decltype是一个关键字,用于推导表达式的类型。它可以在编译时获取表达式的类型,并将其作为变量的声明类型或函数的返回类型。

下面是一些使用decltype的示例:

  1. 推导变量的类型:
int x = 42;
decltype(x) y = x;  // 推导y的类型为int

在这个示例中,使用decltype(x)推导出变量y的类型为int,因为它与变量x具有相同的类型。

  1. 推导表达式的类型:
int a = 10, b = 20;
decltype(a + b) result = a + b;  // 推导result的类型为int

在这个示例中,decltype(a + b)推导出表达式a + b的类型为int,因为它是两个int类型相加的结果。

  1. 推导函数的返回类型:
int getValue() {
  return 42;
}

decltype(getValue()) num = getValue();  // 推导num的类型为int

在这个示例中,decltype(getValue())推导出函数getValue()的返回类型为int,因此变量num的类型也被推导为int

decltype在编写模板或需要从表达式中获取类型信息的情况下特别有用。它能够保留表达式的常量性质、引用性质和cv限定符,从而提供更准确的类型推导。

需要注意的是,decltype并不会执行表达式,它仅仅用于获取表达式的类型。在使用时要确保表达式是有效的,并且不会产生副作用。

3、智能指针

智能指针(Smart pointers)是C++中的一种工具,用于管理动态分配的资源(如堆上的内存),以避免内存泄漏和资源泄漏等问题。智能指针提供了自动化的内存管理,使得资源的释放可以更安全、可靠和方便。

C++中提供了几种智能指针类型,其中最常用的是以下三种:

  1. std::shared_ptr:共享指针是一种引用计数智能指针,允许多个指针共享同一个对象,并在最后一个引用被释放时自动销毁对象。

  2. std::unique_ptr:独占指针是一种独占所有权的智能指针,确保只有一个指针可以访问对象。它提供了高效的移动语义,使得资源的所有权可以进行转移。

  3. std::weak_ptr:弱引用指针是一种不增加引用计数的智能指针,用于解决std::shared_ptr可能引发的循环引用问题。它允许观察一个对象,但不会增加对象的引用计数。

使用智能指针可以避免手动释放资源的繁琐和容易出错的过程,因为它们利用了RAII(Resource Acquisition Is Initialization)的原则,在对象构造和析构的过程中自动管理资源的申请和释放。

以下是一个简单的示例,展示了智能指针的用法:

#include <memory>
#include <iostream>

int main() {
  // 使用 std::unique_ptr 管理动态分配的整数
  std::unique_ptr<int> ptr = std::make_unique<int>(42);

  // 使用 std::shared_ptr 共享指针
  std::shared_ptr<int> sharedPtr = std::make_shared<int>(10);
  std::shared_ptr<int> anotherPtr = sharedPtr;

  // 使用 std::weak_ptr 弱引用指针
  std::weak_ptr<int> weakPtr = sharedPtr;

  // 访问指针所指向的对象
  std::cout << *ptr << std::endl;
  std::cout << *sharedPtr << std::endl;

  return 0;
}

在上述示例中,我们使用了std::unique_ptrstd::shared_ptrstd::weak_ptr来管理不同类型的指针。这些智能指针会在适当的时候自动释放所管理的资源,无需手动调用deletedelete[]来释放内存。

智能指针是一种强大的工具,可以提高C++程序的内存安全性和可维护性。它们是现代C++中推荐使用的内存管理方式之一。

4、右值引用和移动语义

4.1 左值与右值

在C++中,左值(Lvalue)和右值(Rvalue)是表达式的两种基本分类,用于描述表达式的值类别。

  1. 左值(Lvalue):

    • 左值是具名的对象,可以通过其标识符进行访问和修改。
    • 左值可以出现在赋值语句的左侧和右侧,可以被取地址。
    • 示例:变量、数组元素、对象的成员等。
  2. 右值(Rvalue):

    • 右值是指临时的、没有持久性的值,通常是表达式的计算结果。
    • 右值只能出现在赋值语句的右侧,不能被取地址。
    • 示例:字面量(如整数、浮点数、字符串)、临时对象、表达式的结果等。

简单来说,左值是具名的、有持久性的对象,可以被多次引用和修改;而右值是临时的、短暂的值,一般用于计算或传递给函数等临时操作。

在C++11之后,引入了右值引用(Rvalue reference)和移动语义的概念,允许对右值进行特殊的操作和优化,如移动语义的使用、避免不必要的复制等,以提高性能和资源的有效利用。

4.2 引用举例

左值引用(Lvalue reference)和右值引用(Rvalue reference)是C++中用于引用类型的两种引用方式。

左值引用(Lvalue reference)用于绑定到左值,即可以标识一个具名的对象。左值引用使用单个&符号表示,例如:

int x = 10;  // x是一个左值
int& lvalueRef = x;  // 左值引用绑定到x

在上述示例中,lvalueRef是一个左值引用,它绑定到了变量x,因此可以通过lvalueRef修改x的值。

右值引用(Rvalue reference)用于绑定到右值,即临时对象、字面量或表达式的结果。右值引用使用双个&&符号表示,例如:

int&& rvalueRef = 42;  // 右值引用绑定到临时对象

在上述示例中,rvalueRef是一个右值引用,它绑定到了临时对象42。右值引用通常用于支持移动语义,允许高效地转移资源的所有权或实现移动构造函数和移动赋值运算符。

右值引用还与完美转发(perfect forwarding)相关,通过使用右值引用可以保持传递给函数的实参的值类别(左值或右值)不变,从而实现参数的完美转发。

总结起来,左值引用用于绑定左值,右值引用用于绑定右值。左值引用是对对象的别名,而右值引用则提供了对临时对象和可移动对象的特殊引用方式,用于实现高效的资源管理和完美转发。

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。

右值引用总结:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。

4.3 移动构造

移动构造(Move construction)是C++中的一个特性,用于通过使用右值引用(Rvalue reference)来高效地从一个对象“移动”资源到另一个对象。

移动构造函数通常用于在对象的构造过程中,从一个临时对象或即将销毁的对象中“窃取”资源,而不是进行昂贵的复制操作。这对于具有大量数据或动态分配内存的对象特别有用,可以避免不必要的内存复制和分配。

移动构造函数的定义遵循以下形式:

ClassName(ClassName&& other) noexcept
{
  // 从other中移动资源到当前对象
  // 对other进行必要的清理或标记,确保其析构不会造成资源重复释放
}

在移动构造函数中,参数类型为ClassName&&,表示一个右值引用。通过使用右值引用,可以确保移动构造函数只会绑定到右值(如临时对象或即将销毁的对象)上,而不会绑定到左值。

移动构造函数的实现通常涉及以下步骤:

  1. 从源对象中窃取资源:例如,可以通过将指针或句柄的所有权从源对象转移到目标对象,而无需进行昂贵的数据复制。

  2. 对源对象进行适当的清理或标记:例如,将源对象置于一种无效或已被移动状态,以避免其析构过程中重复释放资源。

移动构造函数常常与右值引用和移动语义搭配使用,以提高性能和资源的有效利用。它是现代C++中优化对象构造和资源管理的重要特性之一。

4.4 移动语义

移动语义(Move semantics)是C++中的一个重要概念,用于优化对象的拷贝操作,特别是对于具有资源所有权的对象。

传统的拷贝语义会对对象进行完全的复制,包括复制底层资源,这在某些情况下可能是低效的。移动语义通过使用右值引用(Rvalue reference)和移动构造函数(Move constructor)来实现资源的转移,从而避免了不必要的资源复制和分配。

移动语义的主要思想是在移动对象时,尽可能地“窃取”(移动)资源,而不是进行昂贵的复制操作。对于可以移动的对象,移动语义可以提供显著的性能提升。

通过使用右值引用和移动构造函数,可以将资源的所有权从一个对象转移到另一个对象,而无需进行深层次的数据复制。移动构造函数利用右值引用绑定到右值上,并在构造过程中从源对象“窃取”资源,使得资源的所有权被转移至目标对象。

例如,考虑以下情况:

std::vector<int> createVector()
{
  std::vector<int> vec;
  // 假设在vec中添加大量数据
  return vec;  // 返回vec
}

在上述示例中,返回类型为std::vector<int>的函数createVector()创建了一个局部的std::vector对象,并将其返回。在传统的拷贝语义中,返回操作会对整个std::vector对象进行复制,包括其中的数据。然而,通过使用移动语义,可以避免不必要的数据复制,而是直接将资源(内存)的所有权从局部对象转移到函数的调用方:

std::vector<int> vec = createVector();  // 使用移动语义,避免不必要的复制

在这个例子中,由于返回的对象是一个临时对象(右值),移动语义允许将其资源(内存)有效地移动到变量vec中,而无需进行数据的复制。

移动语义不仅可以应用于容器,还可以应用于其他具有资源所有权的对象,如动态分配的内存、文件句柄等。它提供了一种高效的资源管理方式,使得程序可以更好地利用资源并避免不必要的开销。

4.5 使用场景

左值引用和右值引用在不同的场景下有不同的使用方式和目的。

左值引用的主要使用场景包括:

  1. 传统的对象引用:左值引用常用于函数参数和返回类型,允许在函数中对对象进行修改或传递对象的引用。

  2. 容器和数据结构的访问:左值引用可以用于访问容器(如std::vectorstd::map)中的元素,以便进行修改或检索。

  3. 对象的拷贝和赋值:左值引用常用于对象的拷贝构造函数和赋值运算符的实现中。

右值引用的主要使用场景包括:

  1. 移动语义:右值引用允许将资源(如动态分配的内存)的所有权从一个对象转移到另一个对象,实现高效的资源管理,避免不必要的数据复制。

  2. 完美转发:右值引用在模板和泛型编程中广泛应用,用于实现完美转发(perfect forwarding),即将参数原封不动地传递给其他函数,保持其值类别不变。

  3. 临时对象的优化:右值引用可以用于优化临时对象的创建和销毁过程,避免不必要的拷贝开销。

总的来说,左值引用用于传统的对象引用和修改操作,而右值引用用于移动语义、完美转发和临时对象的优化。它们在C++中共同提供了更灵活、高效和安全的资源管理和函数调用方式。

5、可变参数模板

C++11引入了可变参数模板(Variadic templates)的特性,允许函数和类模板接受可变数量的参数。可变参数模板是一种在编译时处理不确定数量参数的方式,它为编程提供了更大的灵活性和通用性。

可变参数模板的基本语法如下:

template <typename... Args>
void myFunction(Args... args)
{
  // 使用args进行操作,可以是展开、迭代或其他处理方式
}

在上述示例中,Args是一个模板参数包(template parameter pack),用于接受任意数量的模板参数。args是一个函数参数包(function parameter pack),用于接受相应数量的函数参数。

可变参数模板的使用方式包括:

  1. 展开参数包(Expanding the parameter pack):使用递归、展开等方式在编译时对参数包进行处理。例如,可以使用递归模板函数对每个参数进行操作,直到参数包为空。

  2. 递归实例化(Recursive instantiation):使用递归实例化来处理参数包中的每个参数。通过逐个实例化每个参数,可以实现递归展开的效果。

  3. 递归调用(Recursive invocation):在函数模板内部递归调用自身,以处理参数包中的每个参数。这允许对参数包中的每个参数执行相同的操作。

可变参数模板的使用非常灵活,它可以适应各种不同的需求。例如,可以定义接受不同类型参数的通用函数,或者定义递归的数据结构,以适应可变数量的元素。

以下是一个简单的示例,展示了可变参数模板的使用:

#include <iostream>

// 递归展开参数包的函数
template<typename T>
void printArguments(T arg)
{
  std::cout << arg << std::endl;
}

template<typename T, typename... Args>
void printArguments(T arg, Args... args)
{
  std::cout << arg << " ";
  printArguments(args...);  // 递归调用,展开参数包
}

int main()
{
  printArguments(1, "hello", 3.14, 'c');

  return 0;
}

在上述示例中,printArguments函数使用可变参数模板,通过递归展开参数包,打印传递给函数的所有参数。运行该程序将输出:1 hello 3.14 c,每个参数都用空格分隔开来。

可变参数模板为C++中的泛型编程提供了更大的能力,使得编写通用的、可适应不确定参数数量的函数和类模板变得更加方便和灵活。

6、lambda表达式

随着C++语法的发展,人们开始觉得,每次为了实现一个algorithm算法, 都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名, 这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式

Lambda表达式是一种匿名函数形式,它允许在需要函数对象时,直接在代码中定义一个简洁的函数。

6.1 语法格式

Lambda表达式的基本语法如下:

[capture list](parameters) -> return_type {
    // 函数体
}

其中:

  • capture list(捕获列表)用于捕获外部变量,可以是空的、值捕获(使用变量的副本)或引用捕获(使用变量的引用)。
  • parameters(参数列表)用于定义Lambda函数的参数。
  • return_type(返回类型)是可选的,可以自动推导出来。

下面是一个简单的示例,展示了Lambda表达式的使用:

#include <iostream>

int main() {
    int x = 42;

    // Lambda表达式打印传入参数的两倍
    auto printDouble = [](int num) {
        std::cout << num * 2 << std::endl;
    };

    printDouble(x);  // 输出:84

    return 0;
}

在上述示例中,我们定义了一个Lambda表达式printDouble,它接受一个整数参数num,并打印出其两倍的值。Lambda表达式被赋值给了一个自动推导的变量printDouble,然后我们调用该Lambda函数并传递参数x

Lambda表达式还可以与标准算法函数(如std::for_eachstd::transform)等结合使用,提供一种简洁而灵活的方式来定义函数对象,避免显式编写函数或函数对象的定义。

Lambda表达式的捕获列表允许在函数体中使用外部作用域的变量,从而实现了闭包(Closure)的特性。它使得编写更为紧凑、内聚的代码成为可能,并且对于一些需要临时函数对象的场景非常方便。

6.2 捕获列表

Lambda捕获列表(Capture list)用于指定Lambda表达式中的外部变量的可见性和使用方式。捕获列表出现在Lambda表达式的开头,位于参数列表之前。

捕获列表允许Lambda表达式访问和使用外部作用域的变量,包括局部变量、函数参数、全局变量等。它提供了对这些外部变量的副本或引用的访问权限,使得Lambda函数可以在其函数体中使用这些变量。

捕获列表有以下两种主要形式:

  1. 值捕获(Value capture):

    • [var]:按值捕获变量var,在Lambda函数内部创建一个变量的副本。
    • [=]:按值捕获所有外部变量,创建它们的副本。
    • [=, &var]:按值捕获所有外部变量,并对变量var进行引用捕获。
    • [&, var]:按引用捕获所有外部变量,并对变量var进行值捕获。
  2. 引用捕获(Reference capture):

    • [&var]:按引用捕获变量var,在Lambda函数内部直接使用外部变量。
    • [&]:按引用捕获所有外部变量,直接使用它们。

示例:

int x = 42;
int y = 10;

// 值捕获
auto lambda1 = [x, y]() {
    // 使用x和y的副本
    // ...
};

// 引用捕获
auto lambda2 = [&x, &y]() {
    // 直接使用x和y的引用
    // ...
};

// 按引用捕获所有外部变量
auto lambda3 = [&]() {
    // 直接使用所有外部变量的引用
    // ...
};

// 按值捕获所有外部变量,并对x进行引用捕获
auto lambda4 = [=, &x]() {
    // 使用所有外部变量的副本,并对x进行引用
    // ...
};

需要注意的是,捕获列表中的变量在Lambda函数内部被视为只读(除非使用mutable关键字),无法直接修改。对于值捕获而言,外部变量的副本在Lambda函数创建时被复制,而对于引用捕获,则直接引用外部变量。

Lambda捕获列表允许在Lambda函数体内访问外部变量,使得Lambda表达式具有了闭包(Closure)的特性,可以在其函数体内部操作和使用外部作用域的变量。这提供了一种便捷的方式来创建具有自包含状态的函数对象。

7、包装器

7.1 没有包装器

当没有使用函数包装器时,让我们看看一个简单的示例来比较结果。

考虑以下目标函数divide,用于将两个整数相除并返回结果:

int divide(int a, int b) {
    return a / b;
}

现在,我们想要实现一个包装器,用于在目标函数调用前后输出额外的信息。

使用函数包装器的情况下,可以这样实现:

#include <iostream>

template<typename Func>
void wrapper(Func func, int a, int b) {
    std::cout << "Before function call" << std::endl;
    int result = func(a, b);
    std::cout << "After function call" << std::endl;
    std::cout << "Result: " << result << std::endl;
}

int main() {
    wrapper(divide, 10, 2);
    return 0;
}

输出结果:

Before function call
After function call
Result: 5

在这个例子中,函数包装器wrapper用于调用目标函数divide,并在调用前后输出信息。它提供了前置和后置处理的功能。

如果没有使用函数包装器,而直接调用目标函数divide,则没有额外的处理或输出信息,只有目标函数的结果:

int result = divide(10, 2);
std::cout << "Result: " << result << std::endl;

输出结果:

Result: 5

在这种情况下,缺少了包装器提供的前置和后置处理,导致无法在调用前后输出额外的信息。

通过比较这两种情况,可以看到函数包装器提供了一种方便的方式来添加额外的逻辑或修改函数行为,使得代码更具灵活性和可扩展性。

7.2 函数包装器

函数包装器(Function wrapper)是一种将函数进行封装或包装的技术,可以在函数调用的前后添加额外的逻辑、修改函数的行为或提供新的接口。

函数包装器通常通过以下几种方式实现:

  1. 函数指针包装器:使用函数指针来包装目标函数,并在调用前后执行额外的操作。可以通过定义一个接受函数指针的函数来实现包装器的调用。

  2. 函数对象包装器:使用函数对象(可调用对象)来包装目标函数,通过重载函数调用运算符operator()来实现包装器的逻辑。函数对象可以是普通函数对象、函数对象类或Lambda表达式。

  3. Lambda表达式包装器:使用Lambda表达式来包装目标函数,并在Lambda表达式中添加额外的逻辑。Lambda表达式可以直接作为函数包装器使用。

函数包装器的常见用途包括:

  • 前置和后置处理:在函数调用之前或之后执行额外的操作,如日志记录、性能统计、参数验证等。
  • 异常处理:在函数调用过程中捕获异常并进行处理,提供异常安全的接口。
  • 记录函数调用历史:跟踪函数的调用记录,用于调试或追踪程序执行流程。
  • 功能扩展:在现有函数的基础上扩展功能,例如添加缓存、权限验证等。

以下是一个简单的示例,展示了使用函数对象(函数指针)作为函数包装器的方式:

#include <iostream>

// 包装器函数接受函数指针和参数,并在调用前后添加额外的逻辑
template<typename Func>
void wrapper(Func func, int arg)
{
    std::cout << "Before function call" << std::endl;

    // 调用目标函数
    func(arg);

    std::cout << "After function call" << std::endl;
}

// 目标函数
void myFunction(int arg)
{
    std::cout << "Inside target function: " << arg << std::endl;
}

int main()
{
    // 使用函数包装器调用目标函数
    wrapper(myFunction, 42);

    return 0;
}

在上述示例中,我们定义了一个函数包装器wrapper,它接受一个函数指针和一个参数,并在调用目标函数之前和之后输出相应的消息。然后,我们定义了目标函数myFunction,它接受一个整数参数并输出该参数的值。在main函数中,我们使用函数包装器调用目标函数myFunction,并传递参数42

函数包装器提供了一种灵活的方式来扩展函数的功能或修改函数的行为,可以根据具体的需求选择合适的包装器实现方式。

7.3 bind函数

std::bind是C++标准库中的一个函数模板,用于创建一个函数对象(函数指针或函数对象)的部分应用(Partial Application)。它可以将一个可调用对象(如函数、函数指针、成员函数指针、函数对象或Lambda表达式)与一些参数进行绑定,生成一个新的函数对象,这个新的函数对象可以接受更少的参数或具有固定的参数值。

std::bind函数的基本语法如下:

#include <functional>

std::bind(function, arg1, arg2, ...);

其中,function是需要进行绑定的可调用对象,arg1, arg2, ...是要绑定的参数。

以下是一个简单的示例,演示了如何使用std::bind进行部分应用:

#include <iostream>
#include <functional>

void foo(int a, int b, int c) {
    std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
}

int main() {
    auto boundFunc = std::bind(foo, 10, std::placeholders::_1, 20);
    boundFunc(30);  // 调用绑定后的函数对象

    return 0;
}

输出结果:

a: 10, b: 30, c: 20

在这个例子中,我们定义了一个函数foo,它接受三个整数参数。使用std::bind,我们将其中的第一个参数绑定为10,第三个参数绑定为20,而第二个参数则使用了占位符std::placeholders::_1,表示在调用时提供。

通过std::bind生成的boundFunc,可以看到参数b已经被固定为30,然后通过调用boundFunc(30)来执行部分应用后的函数。最终的输出结果为"a: 10, b: 30, c: 20"。

std::bind的灵活性使得我们可以对函数进行更多形式的部分应用,包括固定多个参数、指定参数顺序、使用占位符等。这样可以方便地创建新的函数对象,适应不同的调用需求。

8、线程库

C++标准库提供了线程库(Thread Library)用于多线程编程。线程库包含在<thread>头文件中,并提供了创建、管理和同步线程的相关功能。

  1. 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的 状态。
  2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
  3. 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。 线程函数一般情况下可按照以下三种方式提供: 函数指针 lambda表达式 函数对象
  4. thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即将一个 线程对象关联线程的状态转移给其他线程对象,转移期间不意向线程的执行。
  5. 可以通过jionable()函数判断线程是否是有效的,如果是以下任意情况,则线程无效:采用无参构造函数构造的线程对象 线程对象的状态已经转移给其他线程对象 线程已经调用jion或者detach结束

下面是线程库中常用的一些类和函数:

  1. std::thread:代表一个线程对象,可用于创建和管理线程。可以通过传递可调用对象(函数、函数指针、Lambda表达式等)作为参数来创建线程。

  2. std::this_thread:提供了一些与当前线程相关的操作,如sleep_for(让当前线程休眠一段时间)、yield(使当前线程放弃剩余时间片)等。

  3. std::mutex:提供了互斥锁(Mutex)的功能,用于保护共享数据的访问,确保在同一时间只有一个线程能够访问被锁定的数据。

  4. std::condition_variable:条件变量(Condition Variable)用于线程间的同步和通信。它可以阻塞一个或多个线程,直到某个条件满足,并在条件满足时通知等待的线程继续执行。

  5. std::atomic:提供了原子操作的类型,用于在多线程环境中对共享数据进行原子操作,避免竞态条件(Race Condition)。

  6. std::futurestd::promise:用于在异步编程中获取线程函数的返回值。std::future表示一个可能还未完成的异步操作的结果,而std::promise则用于设置异步操作的结果。

除了上述类和函数,线程库还提供了其他的工具和辅助函数,以支持多线程编程的各个方面,如线程的创建和销毁、线程的同步与互斥、原子操作、线程局部存储等。文章来源地址https://www.toymoban.com/news/detail-561984.html

到了这里,关于【C++】C++11特性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】C++11新特性(下)

      上篇文章(C++11的新特性(上))我们讲述了C++11中的部分重要特性。本篇接着上篇文章进行讲解。本篇文章主要进行讲解: 完美转发、新类的功能、可变参数模板、lambda 表达式、包装器 。希望本篇文章会对你有所帮助。 文章目录 一、完美转发 1、1 实例详解  1、2 应用场

    2024年02月10日
    浏览(33)
  • 【C++】—— 简述C++11新特性

    序言: 从本期开始,我将会带大家学习的是关于C++11 新增的相关知识!废话不多说,我们直接开始今天的学习。 目录 (一)C++11简介 (二)统一的列表初始化 1、{}初始化 2、std::initializer_list (三)声明 1、auto 2、decltype 3、nullptr (四)范围for循环 (五)STL中一些变化 总

    2024年02月10日
    浏览(33)
  • 【C++】c++11新特性(一)

    目录 { }列表初始化 内置类型---对单值变量及数组的初始化 列表初始化时进行的类型转换 自定义类型---对类对象或结构的初始化 initializer_list 1. 定义接受 initializer_list 参数的构造函数 2. 在函数中使用 initializer_list 参数 3. 使用 initializer_list 与 vector 等容器 用于推断类型的关键

    2024年04月29日
    浏览(30)
  • C++ 11新特性之function

    概述         C++ 11标准库引入了许多创新功能,其中之一便是std::function。作为函数对象容器,std::function允许开发者在编译时不知道具体类型的情况下,存储和传递任意可调用对象,极大地提升了代码的灵活性和可复用性。本文将详细探讨std::function的工作原理、使用场景

    2024年01月18日
    浏览(45)
  • 【C++】C++11的新特性(上)

       C++11作为C++标准的一个重要版本,引入了许多令人振奋的新特性,极大地丰富了这门编程语言的功能和表达能力。本章将为您介绍C++11的一些主要变化和改进,为接下来的章节铺垫。    文章目录 引入 一、列表初始化 1、1 {} 初始化 1、2 std::initializer_list的介绍 二、声明

    2024年02月11日
    浏览(35)
  • 【C++】C++11新特性的讲解

    新特性讲解第一篇~  文章目录 前言 一、较为重要的新特性 1.统一的初始化列表 2.decltype 3.右值引用+移动语义 总结 C++11 简介 : 在 2003 年 C++ 标准委员会曾经提交了一份技术勘误表 ( 简称 TC1) ,使得 C++03 这个名字已经取代了 C++98 称为 C++11 之前的最新 C++ 标准名称。不

    2024年02月09日
    浏览(39)
  • 【C++基础】C++11的新特性

    C++新特性主要包括包含语法改进和标准库扩充两个方面,主要包括以下11点 1、语法的改进 1)统一的初始化方法 在 C++11 中,可以直接在变量名后面跟上初始化列表,来进行对象的初始化 2)成员变量默认初始化 3)auto** 用于定义变量,编译器可以自动判断的类型(前提

    2024年02月16日
    浏览(30)
  • C++ 11新特性之语法甜点4

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是最后一篇关于“语法甜点”的文章。 语法甜点16:新的字符串字面值         C++ 03提供了两种字符串字面值

    2024年02月20日
    浏览(33)
  • C++ 11新特性之语法甜点3

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是第三篇关于“语法甜点”的文章。 语法甜点11:非成员的begin和end         在C++ 03中,标准容器都提供了

    2024年02月20日
    浏览(37)
  • C++ 11新特性之可变参数模板

    概述         随着C++ 11标准的发布,C++语言获得了许多强大的新特性,其中一项显著提升灵活性和实用性的创新便是可变参数模板。这一特性极大地扩展了模板在处理不定数量类型或值参数时的能力,为开发者提供了更为强大且灵活的泛型编程工具。 工作机制       

    2024年02月22日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包