[C/C++] -- C++11相关内容

一:声明

  • auto:

auto 是 C++11 引入的一个关键字,用于自动推断变量的类型。通过使用 auto,编译器可以根据变量的初始化表达式推断其类型,从而减少代码中的重复冗长的类型声明。

简化模板声明:

for(auto p = vec.begin();p!=vec.end();p++)
  • decltype

decltype 是 C++11 引入的一个关键字,用于获取表达式的类型,而不进行实际的表达式求值。它的作用是让编译器在编译时推断表达式的类型,并将其作为编译器生成的类型声明的一部分。

获取变量的类型:

int x = 5;
decltype(x) y; // y 的类型为 int,与 x 的类型相同

获取表达式的类型:

int a = 10;
double b = 20.5;
decltype(a + b) c; // c 的类型为 double,因为 a + b 的类型为 double

结合 autodecltype

int x = 5;
auto y = x; // y 的类型为 int,auto 推断为 int
decltype(auto) z = x; // z 的类型为 int&,decltype(auto) 保留了 x 的引用类型

获取函数返回值类型:

int foo();
decltype(foo()) result; // result 的类型为 foo() 函数返回值的类型
  • auto

    • 简化模板代码,减少模板参数的复杂性。
    • 适用于需要简化类型声明,提高代码可读性的场合。
  • decltype

    • 在需要编写泛型代码时,用于捕获表达式的确切类型。
    • 与模板结合,用于处理复杂的类型推导,例如迭代器或容器中的类型。
    • 在需要保留变量引用类型的场合。
int arr[5] = {1, 2, 3, 4, 5};
decltype(arr) arr_copy; // arr_copy 的类型是 int[5]
auto arr_ref = arr; // arr_ref 的类型是 int*

当我们声明 int arr[5] = {1, 2, 3, 4, 5}; 时,我们创建了一个名为 arr 的数组,其中包含了5个整数元素。

  1. decltype(arr) arr_copy

    这行代码中,decltype(arr) 将返回 arr 的类型,即 int[5],表示一个包含5个整数元素的数组。因此,arr_copy 的类型也是 int[5],它是一个未初始化的包含5个整数元素的数组。
  2. auto arr_ref = arr

    这里使用 auto 推导 arr_ref 的类型。因为 arr 是一个数组,当数组名被用作表达式时,它会自动退化为指向数组首元素的指针(即 int* 类型)。因此,arr_ref 的类型被推导为 int*,表示一个指向整数的指针,指向数组 arr 的首元素。

所以,arr_copy 是一个未初始化的包含5个整数元素的数组,而 arr_ref 是一个指向数组 arr 的首元素的指针。

  • 返回类型后置

在C++11之前,返回类型通常在函数签名的前面指定,例如:

int add(int a, int b) {return a + b;
}

然而,随着C++11的引入,一种新的语法允许在函数签名之后指定返回类型,这被称为返回类型后置(Trailing Return Type)。这种语法对于需要从参数中推导返回类型的情况特别有用,并且在与 decltype 结合时非常方便。

#include <iostream>// 使用返回类型后置来定义一个函数,返回两个输入参数的和。
auto add(int a, int b) -> int {return a + b;
}// 使用返回类型后置和 decltype 来推导返回类型。
template <typename T, typename U>
auto multiply(T a, U b) -> decltype(a * b) {return a * b;
}int main() {std::cout << "Add: " << add(3, 4) << std::endl; // 返回类型是 intstd::cout << "Multiply: " << multiply(3, 4.5) << std::endl; // 返回类型由 decltype 推导return 0;
}
  • 模板别名:using=

在C++11中,引入了 using 关键字用于定义模板别名。using 关键字提供了一种更简洁、易读的方式来定义类型别名,特别是在使用模板时。

using alias_name = type;

alias_name 是你为类型定义的别名,而 type 是你要为其创建别名的类型。using 语句可以用于定义任何类型的别名,包括模板类型。

typedef 可以创建基本类型、指针类型、复合类型和函数指针类型的别名。

using 在 C++11 引入之后,成为了更通用的替代方案,可以用于创建类型别名、模板别名以及模板别名模板。

差别在于,新语法可用于模板部分具体化,但typedef不能

template<typename T>using arr12 = std::arry<T,12>

对于如下声明:

std::array<double,12> a1;
std::array<std::string,12> a2;

可以使用上述具体化模板声明:

arr12<double> a1;
arr12(std::string) a2;
  • nullptr

nullptr 是 C++11 中引入的空指针常量,用于代表空指针。它是一个特殊的字面值,可以被赋值给指针类型,而且不会与整数进行混淆。nullptr 用于替代传统的 NULL 宏,NULL 通常被定义为 0 或者 (void*)0。因为 NULL 的定义可能是 0 或者指针类型的零值,所以在某些情况下,使用 NULL 可能会引起歧义,特别是在函数重载时。

nullptr 的引入解决了这个问题,它是一个明确的指针值,可以用于初始化任何指针类型,而不会与整数进行混淆。

二:智能指针

智能指针是 C++ 中用于管理动态内存的一种工具,它们可以自动管理内存的生命周期,从而减少内存泄漏和悬挂指针等问题。智能指针在 C++11 引入标准库之后变得非常流行。

  1. std::unique_ptr

    • std::unique_ptr 是一种独占所有权的智能指针,它确保在其生命周期结束时自动释放所管理的对象。
    • 每个 std::unique_ptr 拥有对其所指向对象的唯一所有权,当 std::unique_ptr 被销毁时,它会自动释放其所管理的对象。
    • 不能进行拷贝操作,但可以进行移动操作。
  2. std::shared_ptr

    • std::shared_ptr 是一种共享所有权的智能指针,它允许多个指针共享对同一对象的所有权。
    • std::shared_ptr 使用引用计数来跟踪有多少个 std::shared_ptr 共享同一对象。当引用计数为零时,对象会被自动释放。
    • 拷贝 std::shared_ptr 会增加引用计数,而销毁或者重置 std::shared_ptr 则会减少引用计数。
  3. std::weak_ptr

    • std::weak_ptr 是一种弱引用智能指针,它不会增加对象的引用计数,也不会拥有对象的所有权。
    • 通常与 std::shared_ptr 一起使用,用于解决 std::shared_ptr 的循环引用问题。
    • 可以通过 std::weak_ptr 创建 std::shared_ptr,但需要检查 std::weak_ptr 是否过期(即底层对象是否已被释放)。

三:类的修改

  • explicit

explicit 关键字用于防止隐式转换和复制初始化。它可以应用于单参数构造函数和转换函数。

#include <iostream>class MyClass {
public:explicit MyClass(int x) : data(x) {}int getData() const { return data; }private:int data;
};void process(const MyClass& obj) {std::cout << "Data: " << obj.getData() << std::endl;
}int main() {MyClass obj1(42); // 直接初始化,正常process(obj1);    // 调用 process 函数,正常// MyClass obj2 = 42; // 错误!explicit 构造函数禁止复制初始化MyClass obj2 = MyClass(42); // 正确,使用直接初始化process(obj2);              // 调用 process 函数,正常return 0;
}

explicit 阻止了 MyClass 的单参数构造函数被用于隐式转换,从而增强了代码的清晰度和安全性。

  • 类内成员初始化

类内成员初始化是在 C++11 引入的特性,允许在类定义中直接初始化成员变量。这种方式可以确保成员变量在对象创建时就被初始化,提高了代码的可读性和可维护性。

#include <iostream>class MyClass {
public:// 类内初始化成员变量int data = 0;double value = 3.14;// 构造函数MyClass(int d) : data(d) {} // 对于没有在类内初始化的成员变量,可以在构造函数中进行初始化
};int main() {MyClass obj(42);std::cout << "Data: " << obj.data << std::endl;   // 输出:Data: 42std::cout << "Value: " << obj.value << std::endl; // 输出:Value: 3.14return 0;
}

四:模板和STL的修改

为改善模板和标准模板库的可用性。

  • for循环修改

对于内置数组以及包含方法begin()和end()的类(如std::string)和STL容器,基于范围的for循环可简化编写循环的工作。

如要修改可以使用引用类型。

#include <iostream>
#include <vector>
#include <string>int main() {// 修改内置数组int arr[] = {1, 2, 3, 4, 5};for (int &x : arr) {x *= 2; // 将数组中的每个元素乘以 2}// 修改 std::stringstd::string str = "hello";for (char &c : str) {c = toupper(c); // 将字符串中的每个字符转换为大写形式}// 修改 STL 容器(例如 std::vector)std::vector<int> vec = {1, 2, 3, 4, 5};for (auto &num : vec) {num *= 2; // 将容器中的每个元素乘以 2}// 输出修改后的结果for (const auto &x : arr) {std::cout << x << " ";}std::cout << std::endl;std::cout << str << std::endl;for (const auto &num : vec) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
  • 新的STL容器

C++ 新的 STL(标准模板库)容器包括一些在 C++11 标准中引入的以及后续标准中新增的容器。

  1. std::array

    1. 固定大小的数组,与内置数组类似,但提供了更多的功能和安全性。
    2. 它具有固定的大小,在编译时就确定了,因此不支持动态大小调整。
  2. std::forward_list

    1. 单向链表,与 std::list 不同,它只能从头到尾进行迭代,无法逆向迭代。
    2. std::forward_list 在某些情况下比 std::list 更加高效,尤其是对于大量的插入和删除操作。
  3. std::unordered_setstd::unordered_map

    1. 哈希集合和哈希映射,分别对应于 std::set 和 std::map 的无序版本。
    2. 它们使用哈希表来实现,具有 O(1) 的平均插入、查找和删除时间复杂度,但不保证元素的顺序。
  4. std::unordered_multisetstd::unordered_multimap

    1. 允许重复键的哈希集合和哈希映射,分别对应于 std::multiset 和 std::multimap 的无序版本。
    2. 允许插入相同键的多个副本,不保证元素的顺序。
  5. std::tuple

    1. 元组,可以存储多个不同类型的值,并且可以在编译时或运行时访问这些值。
    2. 元组的大小和类型在编译时确定,提供了一种方便的方式来处理多个值。
  6. std::array_viewstd::span(C++20):

    1. 提供了对连续内存区域的非拥有式访问,允许安全地查看数组或容器的一部分,而不复制数据。
    2. std::span 在 C++20 中引入,提供了更多的功能和灵活性。
  • 新的STL方法

cbegin()cend() 返回的是常量迭代器,而 begin()end() 返回的是普通迭代器。主要区别在于:

  1. cbegin() 和 cend()

    • 返回常量迭代器。
    • 用于遍历容器中的元素,但不能修改这些元素。
    • cbegin() 返回指向容器第一个元素的常量迭代器。
    • cend() 返回指向容器尾后位置的常量迭代器。
  2. begin() 和 end()

    • 返回普通迭代器。
    • 可以用于遍历容器中的元素,并且可以修改这些元素。
    • begin() 返回指向容器第一个元素的迭代器。
    • end() 返回指向容器尾后位置的迭代器。
  3. crbegin() 和 crend()

    • 返回常量逆向迭代器。
    • 用于逆序遍历容器中的元素,并且不能修改这些元素。
    • crbegin() 返回指向容器最后一个元素的常量逆向迭代器。
    • crend() 返回指向容器起始位置的常量逆向迭代器。
  4. rbegin() 和 rend()

    • 返回普通逆向迭代器。
    • 用于逆序遍历容器中的元素,并且可以修改这些元素。
    • rbegin() 返回指向容器最后一个元素的逆向迭代器。
    • rend() 返回指向容器起始位置的逆向迭代器。
#include <iostream>
#include <vector>int main() {// 创建一个vectorstd::vector<int> vec = {1, 2, 3, 4, 5};// 使用 cbegin() 和 cend() 遍历容器中的元素(不修改元素)std::cout << "Using cbegin() and cend(): ";for (auto it = vec.cbegin(); it != vec.cend(); ++it) {std::cout << *it << " ";}std::cout << std::endl;// 使用 begin() 和 end() 遍历容器中的元素(可以修改元素)std::cout << "Using begin() and end(): ";for (auto it = vec.begin(); it != vec.end(); ++it) {*it *= 2; // 修改元素的值std::cout << *it << " ";}std::cout << std::endl;// 使用 crbegin() 和 crend() 逆序遍历容器中的元素(不修改元素)std::cout << "Using crbegin() and crend(): ";for (auto it = vec.crbegin(); it != vec.crend(); ++it) {std::cout << *it << " ";}std::cout << std::endl;// 使用 rbegin() 和 rend() 逆序遍历容器中的元素(可以修改元素)std::cout << "Using rbegin() and rend(): ";for (auto it = vec.rbegin(); it != vec.rend(); ++it) {*it *= 2; // 修改元素的值std::cout << *it << " ";}std::cout << std::endl;return 0;
}
  • >>

为避免与运算符>>混淆,C++11要求在声明嵌套模板时使用空格将尖括号分开

std::vector<std::pair<int, std::string> > myVector; // 正确
  • 左值引用

传统C++引用(现称左值引用)使得标识符关联到左值,

左值引用的声明

int x = 5;
int& refX = x;  // refX 是对 x 的左值引用

左值引用作为函数参数

左值引用经常用作函数的参数,可以实现按引用传递,避免不必要的复制。

void increment(int& num) {num++;  // 修改传入的参数
}int main() {int value = 10;increment(value);  // 传入 value 的引用std::cout << value << std::endl;  // 输出 11,因为 value 已被增加return 0;
}

左值引用和赋值

int x = 5;
int y = 10;
int& ref = x;  // ref 引用 x
ref = y;       // 将 x 的值改为 y 的值,即 x 变为 10

虽然引用和取地址都可以用于访问对象,但它们的实现方式和用途不同:

  • 引用是对象的别名,提供了对对象的另一种访问方式,更方便、更安全地操作对象。
  • 取地址是获取对象在内存中的地址,返回的是指向目标对象的指针,可以通过指针来直接访问或修改对象的值,但需要注意指针的正确使用和管理。

引用示例

#include <iostream>int main() {int x = 10;int& ref = x;  // 定义引用 ref,绑定到变量 xstd::cout << "x 的值为:" << x << std::endl;std::cout << "ref 的值为:" << ref << std::endl;ref = 20;  // 通过引用修改 x 的值std::cout << "修改后,x 的值为:" << x << std::endl;std::cout << "修改后,ref 的值为:" << ref << std::endl;return 0;
}

取地址示例

#include <iostream>int main() {int x = 10;int* ptr = &x;  // 取得变量 x 的地址,并将其保存到指针 ptr 中std::cout << "x 的值为:" << x << std::endl;std::cout << "ptr 指向的值为:" << *ptr << std::endl;*ptr = 20;  // 通过指针修改 x 的值std::cout << "修改后,x 的值为:" << x << std::endl;std::cout << "修改后,ptr 指向的值为:" << *ptr << std::endl;return 0;
}
  • 左值引用必须绑定到一个具有持久性的对象,因此不能绑定到临时对象(右值)。
  • 左值引用一般用于实现按引用传递,可以在函数内部修改传入的参数,而不是复制参数的副本。
  • 右值引用

右值引用是 C++11 引入的新特性,用于实现移动语义和完美转发,主要用于优化对象的拷贝和移动操作。

右值引用的声明方式是在类型后面加上 &&,例如 int&& 表示一个右值引用类型。

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

右值引用主要用于绑定到临时对象(右值),通常用于移动语义和完美转发。

int&& rvalue_ref = 5; // 右值引用绑定到临时对象 5int x = 10;
int&& rvalue_ref2 = std::move(x); // std::move 将左值转换为右值引用

std::move 是一个 C++ 中的函数模板,主要用于将对象转换为右值引用,从而支持移动语义。它的主要作用有两个:

  1. 标识对象为右值引用: std::move 将对象转换为右值引用,即使原本是左值,这样就可以在移动构造函数和移动赋值运算符中使用。这使得我们可以使用移动语义,将资源从一个对象转移到另一个对象,而不是进行深拷贝。这样可以提高程序的性能和效率。

  2. 避免不必要的拷贝: 通过将对象转换为右值引用,std::move 告诉编译器该对象可以被移动而不是复制,从而避免不必要的拷贝操作。这对于大型对象或者资源管理类(如动态分配的内存、文件句柄等)尤其有用,因为移动操作通常比复制操作更加高效。

右值引用可以用于传递临时对象或者转移对象的所有权。

void process(int&& data) {// 处理右值引用绑定的临时对象
}int main() {process(10); // 传递临时对象int x = 20;process(std::move(x)); // 转移对象的所有权return 0;
}

移动语义

传统的拷贝操作会导致对象的深拷贝,即复制对象的所有内容,包括动态分配的内存资源,这在处理大型对象时可能效率低下。而移动语义允许在资源管理类中,将资源的所有权从一个对象转移到另一个对象,而不需要进行深拷贝,从而提高了效率。

移动语义的关键在于利用右值引用来识别临时对象(右值),然后通过移动构造函数或者移动赋值运算符来“窃取”这些临时对象的资源,而不是像拷贝构造函数那样创建新的资源副本。

移动语义的实现通常使用 std::move 来将对象转换为右值引用,以便移动构造函数和移动赋值运算符能够正确地被调用。通过移动语义,可以有效地避免不必要的资源复制,提高程序的性能

完美转发

完美转发是一种技术,允许我们在函数中将参数以相同的方式传递给其他函数,保持参数的值类型不变。它通过右值引用和模板来实现。std::forward 是实现完美转发的关键,它能够在保持参数类型不变的同时,将参数转发给其他函数。

 简单的参数传递:如果你只是简单地将参数传递给其他函数,而不需要保留其值类别(即不需要完美转发),那么你可以直接使用传递给你的参数。在这种情况下,不使用 std::forward 是可以的。

template<typename T>
void foo(T arg) {bar(arg);  // 没有保留参数的值类别的必要性
}

 复杂的参数传递:但是,在需要将参数完美转发给其他函数的情况下,特别是在涉及到重载或泛型编程时,使用 std::forward 更为安全。这样可以确保参数的原始值类别被保留,从而正确地调用相关函数。使用 std::forward 可以避免意外地触发拷贝构造函数或移动构造函数,从而提高了代码的效率和安全性。

template<typename T>
void foo(T&& arg) {bar(std::forward<T>(arg));  // 保留参数的值类别
}
#include <iostream>
#include <utility> // for std::move, std::forwardtemplate<typename T>
class MoveOnlyVector {
private:T* data;size_t capacity;size_t size;public:// 默认构造函数MoveOnlyVector() : data(nullptr), capacity(0), size(0) {}// 析构函数~MoveOnlyVector() {delete[] data;}// 移动构造函数MoveOnlyVector(MoveOnlyVector&& other) noexcept : data(std::exchange(other.data, nullptr)), capacity(std::exchange(other.capacity, 0)), size(std::exchange(other.size, 0)) {}// 移动赋值运算符MoveOnlyVector& operator=(MoveOnlyVector&& other) noexcept {if (this != &other) {delete[] data;data = std::exchange(other.data, nullptr);capacity = std::exchange(other.capacity, 0);size = std::exchange(other.size, 0);}return *this;}// 添加元素template<typename U>void push_back(U&& value) {if (size >= capacity) {// 扩展容量size_t new_capacity = (capacity == 0) ? 1 : 2 * capacity;T* new_data = new T[new_capacity];for (size_t i = 0; i < size; ++i) {new_data[i] = std::move(data[i]);}delete[] data;data = new_data;capacity = new_capacity;}data[size++] = std::forward<U>(value);}
};int main() {MoveOnlyVector<std::string> vec;// 添加元素vec.push_back("hello");vec.push_back(std::string("world"));return 0;
}

五:绑定器和函数对象

  • function

std::function 是C++11中的一个模板类,用于封装任意可调用对象,包括函数指针、函数对象、成员函数指针、Lambda表达式等。它提供了一种统一的方式来处理不同类型的可调用对象,并可以在运行时确定其类型。

#include <functional>// 定义一个函数
int add(int x, int y) {return x + y;
}// 使用 std::function 封装一个可调用对象
std::function<int(int, int)> func = add;// 调用封装的函数
int result = func(3, 4);  // result = 7

 std::function 的模板参数是函数的签名,它可以用来存储具有相同参数和返回类型的任意可调用对象。

  • bind

std::bind 是C++11中的一个函数模板,用于部分应用函数参数或重新排序函数参数。它允许您在调用函数时固定某些参数的值,从而创建一个新的可调用对象。绑定器(对STL中bind1st和bind2nd的升级,结合二元函数对象-》一元函数对象)

#include <functional>// 定义一个函数
int add(int x, int y) {return x + y;
}auto add_five = std::bind(add, std::placeholders::_1, 5);
int result = add_five(3);  // result = 3 + 5 = 8

在这个例子中,std::bindadd 函数的第一个参数绑定为占位符 std::placeholders::_1,并将第二个参数固定为 5。返回的 add_five 可调用对象只有一个参数,当调用它时,它会将传递给它的参数与之前绑定的参数一起传递给 add 函数。

  •  Lambda表达式

Lambda表达式是C++11中引入的一种匿名函数语法,它允许您在需要函数对象的地方内联定义函数。Lambda表达式可以捕获外部变量,并具有非常灵活的语法。

#include <iostream>int main() {int x = 3;int y = 4;// 使用Lambda表达式定义一个函数对象auto func = [x, y](int a, int b) {return a * x + b * y;};int result = func(1, 2);  // result = 1 * 3 + 2 * 4 = 11std::cout << "Result: " << result << std::endl;return 0;
}

六:C++语言级别支持的多线程编程

C++的标准库在C++11版本中引入了多线程支持,这为跨平台多线程编程提供了一个统一的接口。这意味着你可以使用C++标准库中的多线程API来编写跨平台的多线程应用程序,而无需依赖于特定的操作系统API(如Windows的CreateThread、Linux的pthread_createclone)。

1. std::thread

std::thread是C++11中提供的类,它封装了操作系统特定的线程创建和管理方式。这使得你可以在不同的操作系统上使用相同的代码创建和管理线程。

#include <iostream>
#include <thread>// 一个简单的线程函数
void threadFunction() {std::cout << "Thread is running" << std::endl;
}int main() {// 创建一个新线程并运行 threadFunctionstd::thread myThread(threadFunction);// 等待线程结束myThread.join();return 0;
}

在这个例子中,创建了一个线程,执行threadFunction函数,然后等待该线程结束。std::thread类允许你创建、启动、加入、分离线程等。

2. std::mutex 和 线程同步:

多线程编程通常需要考虑同步和共享资源的访问。C++标准库提供了各种同步机制,如互斥锁、条件变量等。

#include <iostream>
#include <thread>
#include <mutex>// 共享资源
int counter = 0;
std::mutex mtx;void incrementCounter() {std::lock_guard<std::mutex> lock(mtx);counter++;
}int main() {std::thread t1(incrementCounter);std::thread t2(incrementCounter);t1.join();t2.join();std::cout << "Counter: " << counter << std::endl;return 0;
}

使用std::mutex来保护共享资源counter,确保在多线程环境下不会发生数据竞争

3. 其他同步机制:

除了std::mutex外,C++标准库还提供了std::condition_variablestd::futurestd::promise等,用于实现更复杂的线程同步和通信。

假设正在开发一个网络服务器,需要处理来自多个客户端的请求。我们将使用多线程来同时处理这些请求,以提高服务器的性能和并发能力。

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <queue>
#include <chrono>// 定义一个任务结构体,用于模拟客户端请求
struct Task {int id;Task(int _id) : id(_id) {}
};// 定义一个线程安全的队列,用于存储待处理的任务
template<typename T>
class SafeQueue {
private:std::queue<T> queue_;std::mutex mutex_;
public:void push(const T& item) {std::lock_guard<std::mutex> lock(mutex_);queue_.push(item);}bool try_pop(T& item) {std::lock_guard<std::mutex> lock(mutex_);if (queue_.empty()) {return false;}item = queue_.front();queue_.pop();return true;}
};// 定义一个处理请求的函数
void processRequest(SafeQueue<Task>& tasks, int threadId) {while (true) {Task task(0);if (tasks.try_pop(task)) {// 模拟处理请求的过程std::cout << "Thread " << threadId << " processing task " << task.id << std::endl;// 模拟请求处理时间std::this_thread::sleep_for(std::chrono::seconds(1));} else {// 如果队列为空,则线程等待新的任务到来std::this_thread::yield();}}
}int main() {const int numThreads = 4;SafeQueue<Task> taskQueue;// 创建多个线程来处理请求std::vector<std::thread> threads;for (int i = 0; i < numThreads; ++i) {threads.emplace_back(processRequest, std::ref(taskQueue), i);}// 模拟生成一些任务并将其放入队列中for (int i = 1; i <= 10; ++i) {taskQueue.push(Task(i));std::this_thread::sleep_for(std::chrono::milliseconds(200));}// 等待所有线程完成任务for (auto& thread : threads) {thread.join();}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/313534.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

揭秘区块链的魅力:了解其功能与区块链项目的媒体宣发策略

在数字化的今天&#xff0c;区块链这一先进的技术逐渐成为公众的焦点。然而&#xff0c;许多人对于区块链能干什么&#xff0c;以及如何为区块链项目进行媒体宣发这两大问题抱着疑惑。接下来&#xff0c;让我们一起揭开这个迷雾。 我们先来了解下区块链究竟能做什么。简单地说&…

Tensorflow AutoGraph 的作用和功能

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ TensorFlow AutoGraph 是 TensorFlow 中的一个重要特性&#xff0c;它允许开发者使用普通的 Python 语法编写高效的 TensorFlow 图&#xff08;graph&#xff09;。这意味着开发者可以利用 Python 的易…

Nginx基本使用 反向代理与负载均衡

什么是Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器。 其特点是占有内存少&#xff0c;并发能力强&#xff0c;nginx的并发能力在同类型的网页服务器中表现较好&#xff0c;而且几乎可以做到7*24不间断运行&#xff0c;即使运行数个月也不需要重新启动。 …

多客圈子交友系统 uniapp+thinkphp6适配小程序/H5/app/api全开源,多款插件自选,支持个性定制!

网上交友的优点包括&#xff1a; 1. 方便&#xff1a;网上交友可以随时随地进行&#xff0c;不受时间和空间的限制&#xff0c;方便且高效。 2. 匿名性&#xff1a;网上交友可以实现匿名性&#xff0c;用户可以匿名地搜索、聊天或交换信息&#xff0c;保护个人隐私和安全。 3.…

WEB服务的配置与使用 Apache HTTPD

目录 Web 访问过程&#xff1a; HTTP请求报文&#xff08;请求行、请求头、请求体&#xff09;以及响应报文&#xff1a; 补充&#xff1a; Apache HTTPD服务器的安装与配置 安装&#xff1a; 配置&#xff1a; 修改首页&#xff1a; 打开目录权限 &#xff1a; 重启&#xff1…

(九)Pandas表格样式 学习简要笔记 #Python #CDA学习打卡

目录 一. Pandas表格样式 1&#xff09;举例数据 2&#xff09;字体颜色 3&#xff09;背景高亮 4&#xff09;极值背景高亮 &#xff08;a&#xff09;高亮最大值 highlight_max() &#xff08;b&#xff09;高亮最小值 highlight_min() &#xff08;c&#xff09;同时…

记一次中间件宕机以后持续请求导致应用OOM的排查思路(server.max-http-header-size属性配置不当的严重后果)

一、背景 最近有一次在系统并发比较高的时候&#xff0c;数据库突然发生了故障&#xff0c;导致大量请求失败&#xff0c;在数据库宕机不久&#xff0c;通过应用日志可以看到系统发生了OOM。 二、排查 初次看到这个现象的时候&#xff0c;我还是有点懵逼的&#xff0c;数据库…

Qt : 禁用控件默认的鼠标滚轮事件

最近在写一个模拟器&#xff0c;在item中添加了很多的控件&#xff0c;这些控件默认是支持鼠标滚动事件的。在数据量特别大的时候&#xff0c;及容易不小心就把数据给修改了而不自知。所有&#xff0c;我们这里需要禁用掉这些控件的鼠标滚轮事件。 实现的思想很简单&#xff0c…

OceanBase单机版安装体验

前情提要 上周OceanBase开发者大会过后&#xff0c;作为观察员也来体验一下OB的安装。业内有某个国产安装用了两周&#xff0c;这种其实有点劝退了。话说就是10年前&#xff0c;没搞过Oracle的人也不用两周安装一个数据库啊。今天看看OB的&#xff08;一体化&#xff09;安装。…

linux学习:线程

目录 线程api 创建一条新线程 获取、设置线程的分离属性 获取、设置线程是否继承创建者的调度策略 获取、设置线程的调度策略 获取、设置线程静态优先级 获取、设置线程动态优先级 获取、设置线程栈大小、警戒区大小 退出线程 接合指定线程 给指定线程发送一个取消请…

鸿蒙开发语言_ArkTS开发语言体验_TypeScript语言环境搭建_TS声明和数据类型---HarmonyOS4.0+鸿蒙NEXT工作笔记003

可以看到我们新建的这个项目,有个 @State message: String =Hello ArkTS 这个就是定义了一个变量,可以看到 message是变量名,String是变量类型. 然后我们可以看看它的结构可以看到 build() 下面有个Row,然后再下面有个Column方法,然后,里面就是具体的内容了,首先就是显示了一…

二、OSPF协议基础

基于SPF算法&#xff08;Dijkstra算法&#xff09;的链路状态路由协议OSPF&#xff08;Open Shortest Path First&#xff0c;开放式最短路径优先&#xff09; 目录 1.RIP在大型网络中部署所面临的问题 2.Router ID 3.OSPF的报文 4.OSPF邻居建立过程 5.OSPF报文的确认机制…

在 Linux 上把 Vim 配置为默认编辑器

目录 ⛳️推荐 在 Linux 命令行中编辑 将 Vim 设置为其他程序的默认值 在 Alpine 中编辑电子邮件 总结 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 我使用 Linux 大概有…

3D MINS 多模态影像导航系统

3D MINS多模态影像导航系统&#xff08;Multimodal Image Navigation System&#xff09;是SunyaTech研发的建立在DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;图像基础之上的多模态影像导航系统&#xff0c;集二维影像PACS管理、三维影像层级…

Python函数小知识

目录 一、函数的定义和调用 二、函数参数 三、函数作用域 四、递归函数和匿名函数 一、函数的定义和调用 def 函数名(参数): 自定义函数可以分为有参函数和无参函数 。 函数的作用&#xff1a; 在Python中定义函数可以提高代码的复用率&#xff0c;避免重复的代码&#xff0c;…

Elastic 网络爬虫:为你的网站添加搜索功能

作者&#xff1a;来自 Elastic Lionel Palacin 为了演示如何使用 Elastic 网络爬虫&#xff0c;我们将以一个具体的网站为例&#xff0c;讲解如何在该网站上添加搜索功能。我们将探讨发现网站的方法&#xff0c;并利用 Elastic 网络爬虫提供的功能&#xff0c;以最佳方式准备待…

30 OpenCV 点多边形测试

文章目录 点多边形测试pointPolygonTest示例 点多边形测试 pointPolygonTest pointPolygonTest( InputArray contour,// 输入的轮廓 Point2f pt, // 测试点 bool measureDist // 是否返回距离值&#xff0c;如果是false&#xff0c;1表示在内面&#xff0c;0表示在边界上&a…

Spring Boot中判断轨迹数据是否经过设置的打卡点,且在PGSQL中把点拼接成线,判断某个点是否在线上或在线的50米范围内

问题描述 轨迹数据判断是否经过打卡点&#xff0c;轨迹数据太多&#xff0c;循环判断的话非常消耗内存。解决办法只需要把所有轨迹数据点拼成了一条线&#xff0c;然后只需要循环打卡点即可&#xff0c;打卡点不多&#xff0c;一般不会超过100个&#xff0c;如果多的话&#x…

“一个有趣的C语言代码”分析

“一个有趣的C语言代码” 一个有趣的C语言代码-流浪的海豚-ChinaUnix博客 #include <stdio.h> int print() {printf("hello world!\n");return 0; } int main(void) {long base[0];long* result base3;*(result1) *result;*result (long)print;return 0; …

ARM DMIPS算力说明

ARM DMIPS算力说明 ARM算力参考官网地址 https://en.wikipedia.org/wiki/List_of_ARM_processors Product familyARM architectureProcessorFeatureCache (I / D), MMUTypical MIPS MHzReferenceARM1ARMv1ARM1First implementationNoneARM2ARMv2ARM2ARMv2 added the MUL (mu…