C++编程实战:200个经典例程全解析

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

简介:C++是一种功能全面的编程语言,强调面向对象编程、高效性能和库支持。本集合包含200个C++编程例程,涵盖从基础语法到高级特性的广泛主题,包括但不限于基础语法、面向对象编程、异常处理、标准库使用、STL应用、模板编程、内存管理、文件I/O操作和多线程编程。通过学习这些例程,初学者和经验丰富的开发者都能提升编程技巧,并深入了解C++的设计哲学,为解决实际问题打下坚实基础。 C++经典例程200例

1. C++基础语法深入解析

C++是IT行业中广泛使用的编程语言之一,其复杂性及强大功能使其成为了专业开发人员的首选。在这一章节中,我们将深入探讨C++的基础语法元素,并解析那些构成C++强大核心的语法特性。

首先,我们从C++的基础语法概念开始,包括变量定义、数据类型、运算符以及控制结构等。我们将详细说明如何声明、初始化变量,并展示不同类型的数据如何在程序中进行操作。紧接着,我们将深入学习C++的控制结构,包括条件判断、循环以及函数定义和使用。此部分的内容不仅将涵盖基本的if-else语句和for循环,还将介绍更复杂的控制流,如递归函数和异常处理。

在详细讨论了这些基本概念后,我们将转向指针和引用的操作。这些是C++中独特的特性,它们允许程序以更灵活的方式进行内存管理和参数传递。通过具体实例,我们将展示如何使用指针和引用以提高程序的运行效率和灵活性。本章的结尾将通过解析代码示例来阐明各基础语法点的实际应用,从而为读者提供深入理解C++编程语言的坚实基础。

2. 面向对象编程的艺术

面向对象编程(OOP)是C++的核心特性之一。它不仅提供了一种结构化程序设计的方法,而且通过封装、继承和多态,使得代码更加模块化,易于维护和扩展。本章将深入探讨面向对象编程的各个方面。

2.1 类与对象的本质

2.1.1 类的定义和实现

在C++中,类是创建对象的蓝图或模板。它定义了创建对象时所用到的数据类型以及可以对数据执行的操作。

class MyClass {
private:
    int privateVar; // 私有成员变量,对类外隐藏

public:
    MyClass(int value) : privateVar(value) {} // 构造函数

    void setPrivateVar(int value) {
        privateVar = value; // 公共成员函数,用于设置私有成员变量的值
    }

    int getPrivateVar() const {
        return privateVar; // 公共成员函数,用于获取私有成员变量的值
    }
};

代码逻辑分析: - 类的私有成员变量 privateVar ,只能在类的内部访问。 - 构造函数用于初始化对象的状态。 - setPrivateVar getPrivateVar 成员函数允许我们从类的外部与私有变量进行交互。

2.1.2 对象的创建和使用

对象是根据类定义创建的具体实体。通过对象,我们可以调用类定义的成员函数,实现特定的功能。

int main() {
    MyClass obj(10); // 创建一个MyClass类的对象
    obj.setPrivateVar(20); // 调用对象的成员函数
    int value = obj.getPrivateVar();
    return 0;
}

代码逻辑分析: - obj MyClass 类的一个实例。 - 通过构造函数给 privateVar 赋值。 - 使用成员函数 setPrivateVar getPrivateVar 来修改和读取私有变量。

2.2 继承与多态的实现

继承是OOP中的一个重要概念,它允许我们创建一个新类(派生类)来扩展现有类(基类)的功能。

2.2.1 继承的原理及应用
class BaseClass {
public:
    void baseFunction() {
        std::cout << "BaseFunction called." << std::endl;
    }
};

class DerivedClass : public BaseClass {
public:
    void derivedFunction() {
        std::cout << "DerivedFunction called." << std::endl;
    }
};

代码逻辑分析: - DerivedClass BaseClass 继承而来。 - DerivedClass 可以使用 BaseClass baseFunction 函数。

2.2.2 多态的表现形式和实现机制

多态是指允许不同类的对象对同一消息做出响应的能力。在C++中,多态主要通过虚函数和动态绑定来实现。

class BaseClass {
public:
    virtual void doSomething() {
        std::cout << "BaseClass: doSomething." << std::endl;
    }
};

class DerivedClass : public BaseClass {
public:
    void doSomething() override {
        std::cout << "DerivedClass: doSomething." << std::endl;
    }
};

int main() {
    BaseClass* obj = new DerivedClass();
    obj->doSomething(); // 输出 "DerivedClass: doSomething."
    return 0;
}

代码逻辑分析: - BaseClass 提供了一个虚函数 doSomething 。 - DerivedClass 重写了 doSomething 函数。 - 在 main 函数中,我们使用了基类指针指向派生类对象,并调用了 doSomething 函数。 - 调用通过指针 obj 执行的是 DerivedClass 版本的 doSomething ,展示了多态行为。

2.3 封装与抽象的实践

封装和抽象是面向对象编程中的两个关键概念,它们允许我们隐藏实现细节,并展示对象的行为。

2.3.1 访问控制和数据隐藏

通过访问控制,我们可以限制对类成员的访问,以保护对象的状态。C++中的三种访问权限是 public private protected

class EncapsulatedClass {
private:
    int encapsulatedVar; // 私有成员变量

public:
    EncapsulatedClass(int value) : encapsulatedVar(value) {} // 公共构造函数

    void setEncapsulatedVar(int value) {
        encapsulatedVar = value; // 公共成员函数,用于设置私有变量
    }

    int getEncapsulatedVar() const {
        return encapsulatedVar; // 公共成员函数,用于获取私有变量
    }
};

代码逻辑分析: - encapsulatedVar 成员变量被声明为 private ,确保了数据的隐藏。 - 通过 public 成员函数,我们可以在类外部安全地访问和修改这个私有变量。

2.3.2 抽象类和接口的应用

抽象类和接口是抽象化编程的两种形式,它们定义了与具体实现无关的接口。

class AbstractClass {
public:
    virtual void doSomething() = 0; // 纯虚函数定义
    virtual ~AbstractClass() = default; // 虚析构函数
};

class ConcreteClass : public AbstractClass {
public:
    void doSomething() override {
        std::cout << "ConcreteClass: doSomething." << std::endl;
    }
};

代码逻辑分析: - AbstractClass 包含一个纯虚函数 doSomething ,因此它是一个抽象类。 - ConcreteClass 继承自 AbstractClass 并实现了 doSomething 函数。 - 抽象类允许我们定义一个类族的行为,而具体实现可以留给派生类。

通过本章节的介绍,我们可以了解到C++面向对象编程的核心概念。下一章节,我们将深入探讨异常处理机制,这是现代编程语言不可或缺的一部分。

3. 异常处理机制的探索

异常处理是C++程序设计中不可或缺的一部分,它提供了一种机制来处理程序运行时可能出现的错误和异常情况。通过合理地使用异常处理,可以使程序更加健壮,提高代码的可读性和可维护性。本章将对C++中的异常处理机制进行深入探讨,包括异常的分类、异常捕获和处理策略、标准异常类的应用以及异常与资源管理的关联。

3.1 异常处理的原理

3.1.1 异常的分类和传播

异常是程序运行时的错误或异常情况的表示,它可以是一个错误对象或者一个错误代码。在C++中,异常可以分为同步异常和异步异常。

同步异常通常是由程序内部的错误引起的,比如除以零、数组越界、空指针解引用等,这些问题都是可以通过代码检查发现并处理的。异步异常则是指那些不可预测的、由外部因素引发的异常,如硬件故障、外部信号等。

异常的传播是异常处理中的一个关键概念。当一个异常被抛出时,它会沿着调用栈向上冒泡,直到被一个对应的catch块捕获。如果没有任何catch块能够匹配该异常,程序将调用std::terminate函数终止执行。这表明异常的传播是自底层向上层逐级进行的,直至找到合适的处理点。

3.1.2 异常捕获和处理的策略

异常捕获通常使用try-catch块来完成。程序员需要在可能发生异常的代码周围添加try块,并在之后的catch块中定义异常处理逻辑。

try {
    // 可能抛出异常的代码
} catch (const std::exception& e) {
    // 处理std::exception派生类型的异常
} catch (...) {
    // 处理所有其他类型的异常
}

在上述代码中,try块中包含可能抛出异常的代码。当异常被抛出时,紧接着的catch块将会按顺序尝试捕获该异常。如果异常是std::exception的派生类型,那么第一个catch块会捕获它;否则,最后一个catch块(通常是带省略号的catch)会捕获所有其他类型的异常。

异常处理策略应遵循简单性、明确性和安全性原则。这意味着在异常处理中应尽量减少复杂的控制流,明确异常处理逻辑,并确保异常安全,即不泄露资源,不破坏对象状态,以及保证基本的程序逻辑一致性。

3.2 标准异常类的应用

3.2.1 标准异常类的使用方法

C++标准库提供了大量的异常类,它们都是std::exception类的派生类。标准异常类包括逻辑错误(logic_error),运行时错误(runtime_error)等。它们各自又有更多的派生类,比如invalid_argument、out_of_range、overflow_error等。

使用标准异常类的好处是,它们提供了一套统一的接口,使得异常的处理更加方便和一致。程序员可以根据需要抛出相应的标准异常。

if (denominator == 0) {
    throw std::invalid_argument("denominator cannot be zero.");
}

在上述代码中,如果除数(denominator)为零,则抛出一个std::invalid_argument异常。

3.2.2 自定义异常类的设计

在某些情况下,标准异常类无法满足特定的需求,此时可以设计自定义异常类。自定义异常类应当继承自std::exception,并重载what()函数,以便提供有意义的错误信息。

#include <stdexcept>
#include <string>

class MyCustomException : public std::exception {
private:
    std::string message;
public:
    MyCustomException(const std::string& msg) : message(msg) {}
    virtual const char* what() const throw() {
        return message.c_str();
    }
};

在上述代码中,我们定义了一个MyCustomException类,它继承自std::exception,并添加了一个构造函数来接收自定义消息,并在what()函数中返回这个消息。这样,我们就可以在程序中抛出这个自定义异常并捕获它,以进行特定的错误处理。

3.3 异常与资源管理

3.3.1 异常安全性的保证

异常安全性是衡量程序在抛出异常后,对象状态的正确性和资源管理是否得当的标准。一个异常安全的程序应保证以下三点:

  • 基本承诺 :即使发生异常,程序仍处于有效状态,所有对象的析构函数都被正确调用。
  • 强异常安全 :异常发生后,程序状态保持不变,就像是操作从未发生过一样。
  • 不抛异常安全 :异常发生时,程序保证不抛出异常,所有操作都是不可逆的。

为实现异常安全性,开发者需要仔细设计类和函数,确保对象的构造和析构操作都是异常安全的。

3.3.2 RAII资源管理范式

资源获取即初始化(Resource Acquisition Is Initialization,RAII)是一种利用C++构造函数和析构函数管理资源的编程技术。RAII可以帮助开发者自动管理资源,比如文件、锁、内存等,从而保证异常安全性。

RAII的关键在于创建一个管理资源的类,该类在构造函数中获取资源,在析构函数中释放资源。通过这种方式,资源的生命周期被自动管理,确保了即使在发生异常的情况下资源也能被正确释放。

class FileGuard {
private:
    FILE* file;
public:
    FileGuard(const char* filename, const char* mode) {
        file = fopen(filename, mode);
    }
    ~FileGuard() {
        if (file) {
            fclose(file);
        }
    }
    operator FILE*() { return file; }
};

在上述代码中,FileGuard类负责管理一个文件资源。它的构造函数尝试打开一个文件,并在析构函数中关闭文件。使用FileGuard可以确保文件资源在作用域结束时被正确关闭,无论是否有异常抛出。

{
    FileGuard file("example.txt", "r");
    // 文件操作代码,假设可能会抛出异常
    // ...
}
// 在这里FileGuard的析构函数会被调用,文件被安全关闭

通过使用RAII,开发者可以构建出健壮且易于维护的异常安全代码。它是C++中实现资源管理的推荐方式,也被广泛应用于标准库的设计中,如智能指针std::unique_ptr和std::shared_ptr就是RAII的典型应用。

在下一章节中,我们将继续探讨C++标准库的运用之道,包括输入输出库(I/O)、STL容器以及STL算法的深入应用和性能分析。通过学习这些内容,可以进一步提高C++程序设计的效率和效果。

4. C++标准库的运用之道

4.1 输入输出库(I/O)

4.1.1 标准输入输出流的使用

C++标准库中的输入输出流(I/O)是处理数据和用户交互的重要工具。I/O流库为C++程序员提供了一组丰富的类和函数,用于处理标准输入输出操作。理解如何使用这些流类和操纵符是进行有效C++编程的基础。

首先,C++中的 iostream 库提供了 cin cout cerr clog 四个预定义的流对象,分别对应标准输入、标准输出、标准错误输出和标准日志输出。通过这些对象,我们可以轻松地与用户进行交互和输出调试信息。

#include <iostream>

int main() {
    int number;
    std::cout << "Enter an integer: ";
    std::cin >> number;
    std::cout << "You entered: " << number << std::endl;
    std::cerr << "An error occurred!" << std::endl;
    return 0;
}

在上述代码中,我们使用了 std::cin std::cout 来获取用户输入和输出信息。 std::endl 是一个操纵符,它不仅输出换行符,还会刷新输出缓冲区,确保所有内容都被立即输出到目标设备。

4.1.2 文件流和字符串流的应用

C++标准库中的文件流类是 ifstream ofstream ,分别用于从文件中读取数据和向文件写入数据。字符串流类 istringstream ostringstream 则用于处理字符串中的I/O操作。

#include <fstream>
#include <sstream>
#include <string>

int main() {
    std::ifstream infile("input.txt");
    std::ofstream outfile("output.txt");
    std::string line;
    int lineCount = 0;

    while (std::getline(infile, line)) {
        ++lineCount;
        // 处理每一行...
    }
    infile.close();

    // 将处理后的数据写入输出文件
    outfile << lineCount << " lines were processed." << std::endl;
    outfile.close();

    // 字符串流的使用
    std::string str = "Hello World";
    std::istringstream iss(str);
    std::ostringstream oss;

    std::string word;
    while (iss >> word) {
        oss << word << std::endl;
    }

    std::cout << oss.str();
    return 0;
}

在这段代码中,我们首先创建了文件流对象 infile outfile ,分别用于打开一个名为 input.txt 的文件进行读取和打开一个名为 output.txt 的文件进行写入。字符串流的使用则展示了如何将字符串中的数据读取到变量中,以及将变量数据格式化后存入字符串流。

文件I/O流和字符串流在处理数据时的灵活性和效率使得它们成为C++标准库中不可或缺的一部分,这些流类提供了多种方法和操纵符以满足不同的数据处理需求。

4.2 STL容器详解

4.2.1 序列式容器的使用和性能分析

C++标准模板库(STL)中提供了多种容器,它们是管理数据集合的基础。序列式容器包括 vector list deque 等,它们在存储数据时保持了元素的顺序。每种容器都有自己的优势和适用场景。

std::vector 为例,它是一个动态数组,可以在运行时根据需要自动扩展其大小。 vector 提供了随机访问的能力,并且在大多数操作中表现出色,尤其是尾部插入和删除操作。然而,在随机位置插入和删除元素可能会引起较大的性能开销。

#include <vector>

int main() {
    std::vector<int> numbers;
    numbers.push_back(10);
    numbers.push_back(20);
    numbers.push_back(30);

    // 随机访问
    std::cout << numbers[1] << std::endl;

    // 遍历
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这段代码中,我们使用 std::vector<int> 存储了一组整数,并演示了随机访问和遍历操作。 push_back() 方法用于在向量尾部添加新元素。

序列式容器的性能取决于使用的操作类型和容器的当前状态。在实际开发中,开发者需要根据具体需求选择合适的序列式容器,并进行适当的性能分析。

4.2.2 关联式容器的特性和应用场景

关联式容器如 set multiset map multimap ,它们存储的数据是有序的,这些容器在内部通过特定的数据结构如红黑树( std::map std::set 使用)来维护元素的顺序。

std::map 为例,它是一个基于键值对的容器,其中每个键都是唯一的。 map 在存储数据时会自动排序,因此特别适用于需要快速查找特定键对应的值的情况。

#include <map>

int main() {
    std::map<std::string, int> scores;
    scores["Alice"] = 95;
    scores["Bob"] = 87;
    scores["Charlie"] = 92;

    // 访问元素
    std::cout << "Bob's score: " << scores["Bob"] << std::endl;

    // 遍历
    for (const auto &pair : scores) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

在这个例子中,我们创建了一个 std::map ,其中包含了一些学生的分数。通过键值对的索引方式可以快速访问和修改存储的数据。遍历 map 时,我们使用范围for循环来输出所有的键和值。

关联式容器在实现诸如查找、插入、删除等操作时具有较高的效率,特别适用于那些需要高效的数据管理功能的场合。由于容器内的元素是有序的,这些操作通常能够在对数时间复杂度内完成,这在数据量大的情况下尤为重要。

4.3 STL算法应用

4.3.1 算法的基本分类和特点

STL提供了大量的算法,它们可以被分为多个类别,例如排序算法、搜索算法、修改算法等。这些算法可以用于各种容器中,提供了一种通用和高效的方式来进行数据的处理和操作。

排序算法是STL中最常用的一类算法之一,其中 std::sort 是最基本的排序函数。它使用了快速排序、堆排序或其他高效的排序算法作为实现,根据不同的数据情况和大小自动选择最合适的算法。

#include <algorithm>
#include <vector>

int main() {
    std::vector<int> data = {5, 3, 9, 1, 6};

    // 排序
    std::sort(data.begin(), data.end());

    // 遍历并打印排序后的数据
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

这段代码演示了如何使用 std::sort 函数对一个整数 vector 进行排序,并遍历输出排序后的结果。

STL算法的高度模块化和通用性是其突出的优点,开发者可以根据需要将合适的算法应用于合适的容器,以达到最佳的性能和代码的清晰性。算法的接口设计也遵循一致的模式,通常是接受两个迭代器作为范围的起始和结束,并根据需要接受其他参数。

4.3.2 高级算法的实践和案例分析

STL中的高级算法包括函数式编程风格的算法,如 std::transform std::accumulate 等,它们支持更复杂的数据操作和变换。

std::transform 算法可以应用于容器中的所有元素,对其进行指定的函数操作,并将结果存储在另一个容器中。 std::accumulate 用于计算容器中所有元素的总和。

#include <numeric>
#include <vector>
#include <algorithm>

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

    // 使用std::transform将每个元素乘以2
    std::vector<int> doubled(data.size());
    std::transform(data.begin(), data.end(), doubled.begin(),
                   [](int value) { return value * 2; });

    // 使用std::accumulate计算总和
    int sum = std::accumulate(doubled.begin(), doubled.end(), 0);

    // 输出变换后的数据和总和
    std::cout << "Transformed data: ";
    for (int num : doubled) {
        std::cout << num << " ";
    }
    std::cout << "\nSum: " << sum << std::endl;

    return 0;
}

在这个例子中,我们首先使用 std::transform data 中的每个元素进行乘以2的操作,并将结果存储到 doubled 容器中。然后,我们使用 std::accumulate 计算 doubled 中所有元素的总和。

高级算法的运用需要对算法的功能和参数有深入的理解。开发者需要根据具体问题选择合适的算法,并考虑性能因素,如时间复杂度、空间复杂度以及算法的特性。使用高级算法可以极大地简化代码,提高程序的可读性和效率。

STL算法的种类繁多,每个算法都有其特定的使用场景和优势。通过合理选择和应用STL算法,可以有效地解决各种数据处理任务,从简单的遍历到复杂的数学运算,都可以得到高效、简洁的实现。

以上就是本章的全部内容,希望您能从中获得启发和帮助。

5. C++高级特性与现代编程技巧

5.1 模板编程的精髓

模板编程是C++的核心特性之一,它允许程序员编写与数据类型无关的代码,从而提高代码的复用性。模板编程主要包括函数模板和类模板。

5.1.1 函数模板和类模板的设计

函数模板允许我们创建一个通用的函数,该函数可以处理不同的数据类型,无需为每种数据类型编写重复的代码。它的定义方式是在函数声明前加上关键字 template 和一个模板参数列表。

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

在上面的代码中, max 函数模板比较两个值并返回最大值。 typename T 表示模板参数,可以被任何类型替代。

类模板提供了一种创建与数据类型无关的类的方法。通过使用模板参数,我们可以为类中的数据成员和成员函数指定它们将要操作的数据类型。

template <typename T>
class Stack {
private:
    std::vector<T> v;
public:
    void push(T x) { v.push_back(x); }
    T pop() { return v.back(); }
};

在这个简单的 Stack 类模板中,我们定义了一个可以存储任意类型数据的栈。

5.1.2 模板特化和偏特化的策略

模板特化允许我们为特定的类型提供特殊的实现。当标准模板实现不足以满足特定类型需求时,特化就显得尤为重要。特化可以是全特化,也可以是偏特化。

全特化是对模板的完整替代,而偏特化是模板参数的一个特定组合。

template <typename T>
class Stack<T*> {
private:
    std::vector<T*> v;
public:
    void push(T* x) { v.push_back(x); }
    T* pop() { return v.back(); }
};

template <typename T, int N>
class Stack<T[N]> {
private:
    T arr[N];
public:
    void push(const T (&x)[N]) { /* ... */ }
    T pop() { /* ... */ }
};

在上述示例中, Stack 类模板针对指针类型和数组进行了特化。

5.2 内存管理的高级技巧

内存管理是C++中的一个高级主题,直接影响到程序的性能和安全性。在C++中,我们可以使用智能指针和自定义内存管理器来提高内存使用的效率和安全性。

5.2.1 智能指针和内存泄漏防范

智能指针是RAII(资源获取即初始化)的典型例子,它们在构造函数中获取资源,在析构函数中释放资源,从而防止内存泄漏。

#include <memory>

void processResource(std::unique_ptr<int> res) {
    // 使用资源
}

{
    auto res = std::make_unique<int>(42);
    processResource(std::move(res));
} // res离开作用域,资源自动释放

在上面的代码中, std::unique_ptr 管理了一个整数资源。资源在 unique_ptr 对象被销毁时自动释放。

5.2.2 自定义内存管理器的实现

当标准库提供的内存管理工具不能满足特定需求时,我们可以实现自己的内存管理器。自定义内存管理器需要关注内存分配、释放、对齐以及可能的垃圾回收。

#include <new>

class MyMemoryManager {
public:
    void* allocate(size_t size) {
        // 自定义内存分配逻辑
        return ::operator new(size);
    }
    void deallocate(void* ptr) {
        // 自定义内存释放逻辑
        ::operator delete(ptr);
    }
    // ...其他内存管理相关的方法
};

上述代码展示了如何实现一个简单的内存管理器,其提供了内存分配和释放的方法。

5.3 新标准C++11及以后的特性

C++11引入了大量新特性,使C++成为一个更现代、更易于使用的语言。这些特性包括可变参数模板、lambda表达式、auto关键字等。

5.3.1 C++11新增特性概览

可变参数模板(Variadic Templates)

可变参数模板允许函数或类模板接受任意数量和类型的参数。这在编写泛型代码时非常有用。

template<typename ... Args>
void print(Args ... args) {
    (std::cout << ... << args) << '\n';
}

在上面的代码中, print 函数可以接受任意数量的参数并打印它们。

Lambda表达式

Lambda表达式是创建匿名函数对象的一种简洁方式。Lambda非常适合用于需要函数对象但不想显式定义类的情况。

auto fn = [](int x, int y) { return x + y; };
int result = fn(5, 3);
auto关键字

auto 关键字让编译器自动推导变量的类型,这可以减少模板代码中类型的冗余,使代码更清晰。

auto x = 5; // x的类型被推导为int

5.3.2 C++11及以上版本的实践案例

在实践中,C++11及以后版本的新特性可以用来编写更加简洁、可读性强且高效的代码。

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

int main() {
    std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2};
    // 使用C++11新特性排序
    std::sort(numbers.begin(), numbers.end(), [](int a, int b) {
        return a > b;
    });
    for (int num : numbers) {
        std::cout << num << ' ';
    }
    std::cout << std::endl;
    return 0;
}

这个例子展示了使用lambda表达式和范围for循环对整数向量进行排序并打印结果。

5.4 文件I/O与多线程编程实践

C++对文件I/O和多线程的支持为复杂的应用程序开发提供了可能。现代C++提供了强大的工具,以简化文件操作和并发编程。

5.4.1 文件系统库的使用

C++17引入的文件系统库提供了对文件和目录操作的支持,而无需依赖平台特定的API。

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
    fs::path p{"some_directory"};
    if (fs::exists(p)) {
        for (const auto& entry : fs::directory_iterator{p}) {
            std::cout << entry.path() << '\n';
        }
    } else {
        std::cout << "Directory does not exist!\n";
    }
    return 0;
}

5.4.2 多线程编程的同步机制和设计模式

C++11提供了 <thread> , <mutex> , <condition_variable> 等头文件中的同步机制,允许程序员创建和管理线程,以及解决线程间通信和同步问题。

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

std::mutex mtx; // 用于线程间同步的互斥量
int shared_var = 0;

void print_id(int id) {
    std::lock_guard<std::mutex> lock(mtx);
    shared_var = id;
    std::cout << "thread " << id << '\n';
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(print_id, i);

    for (auto& th : threads)
        th.join();

    return 0;
}

5.4.3 并发算法和线程池的实现

C++17标准库中的并行算法、执行器(executors)和线程池(thread pools)等工具允许开发者更好地控制并发性,以及更有效地使用系统资源。

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

int main() {
    std::vector numbers = { 3, 1, 4, 1, 5, 9, 2 };
    std::for_each(std::execution::par_unseq, numbers.begin(), numbers.end(), [](int& x) {
        x *= 2;
    });

    for (int num : numbers) {
        std::cout << num << ' ';
    }
    std::cout << std::endl;

    return 0;
}

以上代码展示了使用并行算法对向量中的元素进行加倍操作,它利用了并行执行策略 std::execution::par_unseq

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

简介:C++是一种功能全面的编程语言,强调面向对象编程、高效性能和库支持。本集合包含200个C++编程例程,涵盖从基础语法到高级特性的广泛主题,包括但不限于基础语法、面向对象编程、异常处理、标准库使用、STL应用、模板编程、内存管理、文件I/O操作和多线程编程。通过学习这些例程,初学者和经验丰富的开发者都能提升编程技巧,并深入了解C++的设计哲学,为解决实际问题打下坚实基础。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值