深入掌握C++:从基础到并发编程

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C++作为一种广泛使用的编程语言,特别适用于系统软件、游戏开发、高性能计算和嵌入式系统。'Comp345'课程旨在教授学生C++的基础语法、面向对象编程原则、标准模板库(STL)的使用以及高级主题如模板元编程和并发编程。学生将通过实践学习,掌握变量声明、控制流结构、函数、OOP概念(类、对象、封装、继承、多态)、STL容器、算法和函数对象。同时,课程也涉及到C++模板的使用、模板特化,以及C++11及后续版本的并发编程特性。该课程通过实践项目如'Comp345-main'源代码文件,帮助学生将所学知识综合应用,以提高编程技能和解决复杂问题的能力。 Comp345

1. C++基础语法讲解

在探索C++的丰富特性和强大功能之前,掌握基础语法是必不可少的步骤。本章将从最基础的元素出发,逐步介绍C++的语法结构,确保每位读者都能够打下坚实的编程基础。

1.1 基本数据类型和运算符

C++拥有丰富的数据类型,包括整型、浮点型、字符型、布尔型等。理解这些基本数据类型是学习C++的第一步。运算符是编程语言中用于执行数值或逻辑运算的符号,例如加减乘除等。掌握各种运算符的用法,能够帮助我们构建出更加复杂的表达式和语句。

int main() {
    int a = 10; // 整型变量
    double b = 3.14; // 浮点型变量
    char c = 'A'; // 字符型变量
    bool d = true; // 布尔型变量
    // 算术运算符示例
    int sum = a + b; // 加法运算
    int diff = a - b; // 减法运算
    int prod = a * b; // 乘法运算
    double div = a / b; // 除法运算
    return 0;
}

1.2 控制结构与函数

控制结构是编程中实现逻辑判断和循环重复操作的语法基础。C++中的条件语句(如if和switch)以及循环语句(如for和while)构成了控制结构的核心。函数则允许我们将代码封装成可重复调用的模块,是组织代码和实现复用的基础。

// 示例:使用if条件语句进行逻辑判断
int value = 10;
if (value > 0) {
    std::cout << "The value is positive." << std::endl;
}

// 示例:定义并调用函数
void printValue(int num) {
    std::cout << "The number is: " << num << std::endl;
}

int main() {
    printValue(value);
    return 0;
}

通过上述基础的讲解,读者将能够对C++的基本语法有一个直观的认识,并能够编写出简单的程序。下一章将深入探讨面向对象编程的概念,这是C++中一个非常重要的编程范式。

2. 面向对象编程(OOP)教学

2.1 面向对象编程基础

2.1.1 类和对象

在面向对象编程的世界中,类和对象是构建程序的基石。一个类可以被看作是一个蓝图,定义了一类对象共有的属性和方法。而对象则是根据这个蓝图创建出来的具体实例。从C++的视角来审视类和对象,我们可以发现它们是如何支撑起整个OOP框架的。

在C++中,定义一个类的语法如下:

class MyClass {
    // 成员变量
    int myAttribute;

public:
    // 构造函数
    MyClass(int attr) : myAttribute(attr) {}

    // 成员函数(方法)
    void myMethod() {
        // 实现细节
    }
};

2.1.2 封装、继承与多态

封装是面向对象编程的三大特性之一。它是指将数据(属性)和操作数据的方法(行为)绑定在一起,形成一个独立的单元。在C++中,使用public、protected和private关键字来控制类成员的访问权限,从而实现封装。

继承是面向对象编程的另一个关键特性,它允许创建类的层次结构。通过继承,子类可以继承父类的属性和方法,同时还能添加自己的特性和行为或重写继承来的方法。C++支持单一继承和多继承。

class Base {
public:
    virtual void myVirtualMethod() {
        std::cout << "Base class method" << std::endl;
    }
};

class Derived : public Base {
public:
    void myVirtualMethod() override {
        std::cout << "Derived class method" << std::endl;
    }
};

多态性是指能够使用基类的指针或引用来指向派生类的对象,并通过基类的指针或引用调用在派生类中重写的方法。在C++中,多态是通过虚函数实现的。虚函数允许在派生类中重写,使得基类指针或引用能够动态绑定到派生类的实现上。

. . . 封装的代码示例及逻辑分析

下面是一个封装的代码示例:

class Account {
private:
    double balance; // 私有成员变量

public:
    void deposit(double amount) {
        balance += amount; // 只允许通过成员函数修改balance
    }

    double getBalance() const {
        return balance; // 允许外部查询,但不允许修改
    }
};

在这个例子中, balance Account 类的一个私有成员变量,它不能被类的外部直接访问。通过封装,我们只能通过公共成员函数 deposit getBalance 来修改和访问 balance 。这样的设计可以确保 balance 的一致性和安全性,防止外部代码直接修改 balance 导致的数据不一致问题。

. . . 继承的代码示例及逻辑分析

这里是一个简单的继承代码示例:

class Vehicle {
protected:
    int wheels;

public:
    Vehicle(int w) : wheels(w) {}
};

class Car : public Vehicle {
public:
    Car(int w) : Vehicle(w) {}
};

在这个例子中, Car 类继承自 Vehicle 类,并通过继承获得了 Vehicle 类中的 wheels 成员变量和构造函数。 Car 类将继承得到的 wheels 变量作为其属性,并在自己的构造函数中初始化它。

. . . 多态的代码示例及逻辑分析

接下来是一个多态的例子:

class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() {}
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing Rectangle" << std::endl;
    }
};

void drawShapes(std::vector<Shape*> shapes) {
    for (Shape* shape : shapes) {
        shape->draw(); // 动态绑定调用实际类型的draw方法
    }
}

在这个例子中, Shape 是一个抽象基类,它包含了一个纯虚函数 draw Circle Rectangle 类继承自 Shape 类,并重写了 draw 方法。在 drawShapes 函数中,我们接受了一个 Shape 指针的向量,这个向量可以包含 Shape 的任何派生类对象。通过调用 draw 方法,我们能够利用多态性,根据对象的实际类型来调用相应的 draw 方法。

通过这些示例,我们可以看到类和对象在面向对象编程中的核心作用,以及如何通过封装、继承和多态这些基本特性,构建出强大且灵活的C++程序。

3. 标准模板库(STL)的使用

3.1 STL容器和迭代器

3.1.1 序列式容器:vector、list、deque

序列式容器是STL中最基本也是最常用的容器类型之一,它们以线性方式存储元素。在这类容器中,元素的访问和操作主要基于元素在序列中的位置。常见的序列式容器包括vector、list和deque。

vector vector 是一个动态数组,能够高效地进行随机访问操作。它支持在序列尾部快速地插入和删除元素,但在序列中间进行插入和删除操作时效率较低。 vector 适用于元素数量未知,但需要频繁访问任何位置元素的场景。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec; // 创建一个空的vector
    for(int i = 0; i < 10; ++i) {
        vec.push_back(i); // 动态添加元素
    }
    for(int i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << ' '; // 通过索引访问元素
    }
    std::cout << std::endl;
    // 输出: ***
    return 0;
}

代码分析: - #include <vector> :包含了 vector 类的定义。 - std::vector<int> vec; :声明了一个 int 类型的 vector 容器。 - vec.push_back(i); :向 vector 的末尾添加元素。 - std::cout << vec[i]; :通过索引访问 vector 中的元素。

vector 的关键优势在于随机访问的效率,这得益于其内部连续的内存存储方式。但是,需要注意的是, vector 不支持在任意位置高效地进行插入和删除操作。

list list 是一个双向链表结构,提供了在任何位置进行高效插入和删除操作的能力。不同于 vector list 不能进行随机访问,因为其元素不是存储在连续的内存空间中。

#include <iostream>
#include <list>

int main() {
    std::list<int> lst;
    for(int i = 0; i < 10; ++i) {
        lst.push_back(i); // 从尾部添加元素
    }

    for(auto it = lst.begin(); it != lst.end(); ++it) {
        std::cout << *it << ' '; // 通过迭代器遍历list
    }
    std::cout << std::endl;

    // 输出: ***
    return 0;
}

代码分析: - #include <list> :包含了 list 类的定义。 - std::list<int> lst; :声明了一个 int 类型的 list 容器。 - lst.push_back(i); :向 list 的末尾添加元素。 - for(auto it = lst.begin(); it != lst.end(); ++it) :通过迭代器遍历 list 中的元素。

list 容器在进行插入和删除操作时的效率非常高,因此它适合需要在任意位置频繁修改元素的场景。

deque deque (double-ended queue)是一种双端队列,它允许在容器的前端和尾端都能高效地插入和删除元素。 deque 在内部实现了多个动态分配的数组,每个数组存储一部分元素,且元素可以连续存储也可以不连续。这就使得 deque 既具有 list 的灵活性,又能在两端快速访问元素,类似于 vector

#include <iostream>
#include <deque>

int main() {
    std::deque<int> dq;
    for(int i = 0; i < 10; ++i) {
        dq.push_front(i); // 在前端添加元素
    }
    for(int i = 0; i < 10; ++i) {
        dq.push_back(i); // 在尾端添加元素
    }
    for(auto it = dq.begin(); it != dq.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    // 输出: ***
    return 0;
}

代码分析: - #include <deque> :包含了 deque 类的定义。 - std::deque<int> dq; :声明了一个 int 类型的 deque 容器。 - dq.push_front(i); :在 deque 的前端添加元素。 - dq.push_back(i); :在 deque 的尾端添加元素。 - for(auto it = dq.begin(); it != dq.end(); ++it) :通过迭代器遍历 deque 中的元素。

在实际应用中,选择 vector list 还是 deque ,需要根据具体需求和操作特点来决定。例如,如果需要频繁随机访问元素,应该选择 vector 。如果需要频繁在容器中间进行插入和删除操作, list 会是一个更好的选择。而如果需要在两端频繁进行操作,但同时又需要一定的随机访问能力,则 deque 可能是最佳选择。

3.1.2 关联式容器:set、map

关联式容器主要用于存储键值对(key-value pairs),它提供了高效的查找、插入和删除操作,基于红黑树等平衡二叉树实现。它们通常以对数时间复杂度完成这些操作。常见的关联式容器包括set和map。

set set 是一个集合,其内部元素不会重复,且元素是有序排列的。每个元素本身即是键值,又作为值。 set 通常使用红黑树实现,因此其元素总是有序的,且 set 不支持随机访问操作。

#include <iostream>
#include <set>

int main() {
    std::set<int> mySet;
    mySet.insert(5);
    mySet.insert(3);
    mySet.insert(8);
    mySet.insert(8); // 尝试插入重复值,set不存储重复元素

    for (auto it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << std::endl;

    // 输出: 3 5 8
    return 0;
}

代码分析: - #include <set> :包含了 set 类的定义。 - std::set<int> mySet; :声明了一个 int 类型的 set 容器。 - mySet.insert(...); :向 set 中插入元素。 - for(auto it = mySet.begin(); it != mySet.end(); ++it) :通过迭代器遍历 set 中的元素。

set 容器中的元素总是有序的,而且不允许重复。它提供了一些成员函数,比如 lower_bound upper_bound ,用于在对数时间复杂度内查找元素。由于其基于平衡树实现, set 的操作效率在众多数据结构中是非常高效的。

map map 是一个字典容器,它以键值对形式存储数据,每个键值对由键(key)和值(value)组成,且每个键是唯一的。键通常用于快速查找对应的值。 map 同样基于红黑树实现,因此它也是有序的。

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> myMap;
    myMap["apple"] = 1;
    myMap["banana"] = 2;
    myMap["orange"] = 3;

    for(auto it = myMap.begin(); it != myMap.end(); ++it) {
        std::cout << it->first << " : " << it->second << std::endl;
    }

    // 输出:
    // apple : 1
    // banana : 2
    // orange : 3
    return 0;
}

代码分析: - #include <map> :包含了 map 类的定义。 - std::map<std::string, int> myMap; :声明了一个键为 string 类型,值为 int 类型的 map 容器。 - myMap["apple"] = 1; :向 map 中插入键值对。 - for(auto it = myMap.begin(); it != myMap.end(); ++it) :通过迭代器遍历 map 中的元素。

map 容器提供了一种机制,能够将键与值关联起来,让我们可以通过键来快速查找值。它特别适合那些需要查找操作的场景,如数据库系统中的索引实现等。

关联式容器如 set map 在需要高效查找、插入和删除元素的场景中非常有用,但它们通常比序列式容器消耗更多的内存,因为每个元素除了要存储键值对之外,还需要存储额外的平衡树相关数据。

3.2 STL算法与函数对象

3.2.1 常用算法:排序、搜索、变换

STL提供了丰富的算法,这些算法可以在不同的容器上执行操作,而无需关心容器的具体实现细节。这些算法被分类为非修改性序列操作、修改性序列操作、排序和通用数字算法等。以下是一些常用的STL算法。

排序算法 - sort :对容器中的元素进行排序。默认情况下, sort 使用 < 操作符比较元素。 - stable_sort :类似于 sort ,但是它会保持相等元素的相对顺序。 - partial_sort :仅对前 n 个元素进行部分排序。

#include <iostream>
#include <vector>
#include <algorithm> // STL算法库

int main() {
    std::vector<int> vec = {5, 3, 8, 1, 2, 9};
    std::sort(vec.begin(), vec.end()); // 对vector进行排序

    for (int v : vec) {
        std::cout << v << ' ';
    }
    std::cout << std::endl;

    // 输出: 1 2 3 5 8 9
    return 0;
}

搜索算法 - find :在容器中查找是否存在指定的值。 - count :计数容器中与指定值相等的元素的数量。

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    int target = 3;

    auto it = std::find(vec.begin(), vec.end(), target);
    if (it != vec.end()) {
        std::cout << "找到目标值: " << target << std::endl;
    } else {
        std::cout << "未找到目标值: " << target << std::endl;
    }

    // 输出: 找到目标值: 3
    return 0;
}

变换算法 - transform :对容器中的元素应用一个操作,并存储结果到另一个容器中。 - for_each :对容器中的每个元素执行一个操作。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath> // 为了使用 sqrt 函数

int main() {
    std::vector<double> vec = {1.0, 2.0, 3.0, 4.0, 5.0};
    std::vector<double> result(vec.size()); // 创建一个相同大小的vector存储结果

    std::transform(vec.begin(), vec.end(), result.begin(), [](double x) {
        return std::sqrt(x); // 对每个元素求平方根
    });

    for (double r : result) {
        std::cout << r << " ";
    }
    std::cout << std::endl;

    // 输出: 1 1.41421 1.73205 2 2.23607
    return 0;
}

STL算法库提供了高度的灵活性和复用性,使得开发者可以不必重写基础代码,而是专注于问题的解决。对于每种算法,STL都提供了大量的重载版本,以适应不同的需求。例如, sort 算法可以通过自定义比较函数来进行复杂类型的排序。

3.2.2 函数对象和仿函数

仿函数(Functors),又称为函数对象,是重载了 operator() 的一种对象类型。它们在某些方面表现得就像函数一样,但它们是类的实例,并且可以持有状态。

#include <iostream>
#include <vector>
#include <algorithm>

class Square {
public:
    int operator()(int x) {
        return x * x;
    }
};

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::transform(vec.begin(), vec.end(), vec.begin(), Square());

    for (int v : vec) {
        std::cout << v << ' ';
    }
    std::cout << std::endl;

    // 输出: 1 4 9 16 25
    return 0;
}

代码分析: - class Square :定义了一个仿函数类。 - Square() :重载了 operator() ,使得该类的对象可以像函数一样被调用。 - std::transform(..., Square()) :使用 Square 对象作为转换函数。

仿函数的使用非常广泛,特别是在需要算法操作时需要保持状态的情况下,比如当一个操作依赖于之前的状态或者需要保存状态供以后使用时。STL提供了一些预定义的仿函数,例如 std::plus std::minus std::multiplies 等,用于简化常用的数学运算。此外,自定义仿函数可以像任何其他对象一样被复制和传递,这使得它们在STL算法中非常灵活和强大。

STL算法和仿函数的结合,为编程提供了一种强大的抽象机制,它允许开发者以更抽象的方式编写通用代码,从而提高代码的复用性和效率。通过使用STL算法和仿函数,开发者可以快速实现复杂的数据处理逻辑,而且代码更加简洁和易于维护。

4. 模板元编程原理

4.1 模板基础与特化

4.1.1 函数模板和类模板

模板是 C++ 中的强大特性,它允许编写与数据类型无关的代码。函数模板允许我们创建一个可以与多种数据类型一起工作的函数,而类模板则允许我们创建一个可以与多种数据类型一起工作的类。

函数模板的基本语法:

template <typename T>
T max(T a, T b) {
    return a > b ? a : b;
}

在上面的代码中, typename T 是一个模板参数,它将在函数调用时被实际的数据类型所替代。函数 max 可以用于比较两个相同类型的值并返回较大的一个。

类模板的基本语法:

template <typename T>
class Stack {
public:
    void push(const T& element);
    void pop();
    T top() const;
    bool isEmpty() const;
private:
    std::vector<T> elems; // 内部使用 STL vector 存储元素
};

在这个例子中, Stack 是一个类模板,它使用了 std::vector<T> 来存储元素。 T 是模板参数,用户在创建 Stack 对象时需要指定具体的数据类型。

4.1.2 模板特化和偏特化

模板特化是模板编程中的一个高级特性,它允许我们为特定的类型或类型组合提供定制的模板实现。模板特化有两种形式:完全特化和偏特化。

完全特化:

template <>
int max<int>(int a, int b) {
    return a > b ? a : b;
}

在这个例子中,我们为 int 类型完全特化了 max 函数模板。这意味着当 max 函数被 int 类型调用时,将会使用这个特化版本。

偏特化:

template <typename T1, typename T2>
class Pair {
public:
    Pair(const T1& a, const T2& b) : first(a), second(b) {}
    T1 first;
    T2 second;
};

// 偏特化版本,当 T1 和 T2 相同时使用
template <typename T>
class Pair<T, T> {
public:
    Pair(const T& a, const T& b) : first(a), second(b) {}
    T first;
    T second;
};

偏特化允许我们针对模板参数的某些特定组合提供不同的实现。在这个例子中,我们为 Pair 类模板提供了一个偏特化版本,它要求两个模板参数必须是相同的类型。

4.2 模板元编程技术

4.2.1 编译时计算

模板元编程(TMP)是 C++ 中利用模板进行编译时计算的一种编程范式。它允许在编译阶段执行复杂的计算,从而生成高效的运行时代码。这使得模板元编程成为了性能优化的强大工具。

编译时计算示例:

template<int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    constexpr int fact5 = Factorial<5>::value; // 编译时计算得到 120
    return 0;
}

在这个例子中, Factorial 是一个模板结构体,它递归地计算阶乘。当编译器处理到 Factorial<5>::value 时,它会展开为 5 * 4 * 3 * 2 * 1 的计算过程,最终结果在编译时就已经确定。

4.2.2 静态断言和类型萃取

静态断言( static_assert )是 C++11 引入的一个特性,它允许在编译时检查某些条件是否满足,如果不满足则编译会失败并输出相应的错误信息。

静态断言示例:

template<typename T>
void process(const T& value) {
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // ... 处理逻辑 ...
}

int main() {
    process(42); // 正确
    // process(3.14); // 编译错误,因为这不是整型
}

在这个例子中, process 函数模板使用了 static_assert 来确保传入的类型 T 是一个整型。如果传入的类型不是整型,编译时会失败,并显示一条错误信息。

类型萃取是一种编程技术,它允许我们根据类型属性提取出相关的类型或常量。 std::is_integral 是标准库提供的一个类型萃取模板,它用于检测一个类型是否为整型。

类型萃取示例:

#include <type_traits>

template<typename T>
void doSomething(T& value) {
    using ValueType = typename std::remove_reference<T>::type;
    // ... 根据 ValueType 进行特定操作 ...
}

int main() {
    int a = 10;
    doSomething(a); // ValueType 是 int
    return 0;
}

在这个例子中, doSomething 函数模板利用 std::remove_reference 来移除 T 的引用属性,并将其作为 ValueType 使用。这是一种常见的类型萃取技术,用于获取原始数据类型。

模板元编程是一个深奥而强大的主题,它在C++中开辟了一条既快速又高效的代码执行路径,是每个C++高级开发者都应该掌握的技巧。通过编译时计算和类型萃取等技术,可以大幅优化程序的性能,并在编译阶段就解决潜在的类型问题。

5. 并发编程与多线程

并发编程是现代编程语言中的一个重要特性,它使得我们可以编写能够同时执行多个任务的程序。C++在C++11标准中引入了对并发编程的原生支持,大大简化了多线程程序的编写,提升了程序执行的效率和吞吐量。在本章中,我们将探讨C++中的多线程基础以及C++11并发特性的应用。

5.1 多线程基础

多线程允许程序将一个大的任务切分成多个小任务,并在不同的处理器核心上同时运行这些任务。这样可以有效地提高程序的性能,特别是在CPU密集型任务中。

5.1.1 线程的创建与管理

C++11中引入了 <thread> 头文件,为线程的操作提供了简单的API。

#include <iostream>
#include <thread>

void thread_function() {
    // 线程将要执行的代码
    std::cout << "Hello from thread!\n";
}

int main() {
    std::thread t(thread_function); // 创建一个线程对象
    std::cout << "Hello from main!\n";
    t.join(); // 等待线程完成
    return 0;
}

上面的示例中,我们创建了一个线程 t ,并且让它执行 thread_function 函数。 join 方法是让主线程等待子线程完成工作后再继续执行。

5.1.2 线程同步机制

由于多个线程可能同时访问同一数据,线程同步机制就显得尤为重要。C++11提供了多种同步工具,例如互斥锁( std::mutex )、条件变量( std::condition_variable )等。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx; // 全局互斥锁
int shared_data = 0;

void increment(int n) {
    for (int i = 0; i < n; ++i) {
        mtx.lock(); // 上锁
        ++shared_data;
        mtx.unlock(); // 解锁
    }
}

int main() {
    std::thread t1(increment, 10000);
    std::thread t2(increment, 10000);
    t1.join();
    t2.join();
    std::cout << "Shared data: " << shared_data << '\n';
    return 0;
}

在上面的示例中,我们使用 std::mutex 来保护对共享资源 shared_data 的访问,确保在任何时刻只有一个线程可以修改它。

5.2 C++11并发特性的应用

C++11中的并发支持不仅包括了线程的创建和管理,还引入了原子操作、内存模型和线程局部存储等高级特性。

5.2.1 原子操作与内存模型

原子操作保证了对变量的操作是不可分割的,从而避免了并发执行时的数据竞争问题。

#include <iostream>
#include <atomic>

std::atomic<int> atomic_data(0);

void atomic_increment(int n) {
    for (int i = 0; i < n; ++i) {
        atomic_data.fetch_add(1, std::memory_order_relaxed); // 原子加操作
    }
}

int main() {
    std::thread t1(atomic_increment, 10000);
    std::thread t2(atomic_increment, 10000);
    t1.join();
    t2.join();
    std::cout << "Atomic data: " << atomic_data << '\n';
    return 0;
}

在上述代码中, std::atomic 用于创建一个原子类型, fetch_add 执行一个原子增加操作, std::memory_order_relaxed 指定了内存模型。

5.2.2 并发算法和线程局部存储

C++11中的 <algorithm> 提供了针对迭代器范围的并发算法。 <thread> 头文件还引入了线程局部存储,允许每个线程拥有自己的数据副本。

#include <iostream>
#include <thread>
#include <vector>
#include <thread>

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> results(numbers.size());
std::thread_specific_ptr<int> t_result(new int);

void process_numbers(int start, int end) {
    for (int i = start; i < end; ++i) {
        t_result.reset(new int); // 为当前线程设置新的数据
        *t_result = numbers[i] * numbers[i]; // 计算平方
        results[i] = *t_result; // 将结果存储在共享数组中
    }
}

int main() {
    std::thread t1(process_numbers, 0, 5);
    std::thread t2(process_numbers, 5, 10);
    t1.join();
    t2.join();
    for (const auto& result : results) {
        std::cout << result << ' ';
    }
    return 0;
}

在此例中,我们利用 std::thread_specific_ptr 创建了线程局部存储,并在多线程环境下安全地存储了计算结果。

通过本章的学习,您已经掌握了C++中多线程编程的基础知识和C++11并发特性的高级应用。接下来的章节将探讨如何将这些知识应用到实际的项目实战中,以及如何提升您的编程综合能力。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:C++作为一种广泛使用的编程语言,特别适用于系统软件、游戏开发、高性能计算和嵌入式系统。'Comp345'课程旨在教授学生C++的基础语法、面向对象编程原则、标准模板库(STL)的使用以及高级主题如模板元编程和并发编程。学生将通过实践学习,掌握变量声明、控制流结构、函数、OOP概念(类、对象、封装、继承、多态)、STL容器、算法和函数对象。同时,课程也涉及到C++模板的使用、模板特化,以及C++11及后续版本的并发编程特性。该课程通过实践项目如'Comp345-main'源代码文件,帮助学生将所学知识综合应用,以提高编程技能和解决复杂问题的能力。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值