玩转CPP第四课

114 阅读5分钟

九.C++中的容器工具

C++容器类主要包括以下几种:vector、list、deque、set、map、unordered_set、unordered_map等。下面分别给出它们的使用样例:

9.1 vector:动态数组,支持随机访问,插入和删除元素时可能需要移动其他元素。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    vec.push_back(6); // 在末尾添加元素
    vec.pop_back();   // 删除末尾元素
    std::cout << "vec[2]: " << vec[2] << std::endl; // 访问元素
    return 0;
}

9.2 list:双向链表,支持快速插入和删除元素,但不支持随机访问。

#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};
    lst.push_back(6); // 在末尾添加元素
    lst.pop_back();   // 删除末尾元素
    lst.push_front(0); // 在开头添加元素
    lst.pop_front();   // 删除开头元素
    return 0;
}

9.3 deque:双端队列,支持在头部和尾部快速插入和删除元素。

#include <iostream>
#include <deque>

int main() {
    std::deque<int> deq = {1, 2, 3, 4, 5};
    deq.push_back(6); // 在末尾添加元素
    deq.pop_back();   // 删除末尾元素
    deq.push_front(0); // 在开头添加元素
    deq.pop_front();   // 删除开头元素
    return 0;
}

9.4 set:基于红黑树实现的有序集合,元素唯一且自动排序。

#include <iostream>
#include <set>

int main() {
    std::set<int> st = {1, 2, 3, 4, 5};
    st.insert(6); // 插入元素
    st.erase(6);  // 删除元素
    return 0;
}

9.5 map:基于红黑树实现的有序映射,键值对存储,键唯一且自动排序。

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};
    mp[4] = "four"; // 插入键值对
    mp.erase(4);     // 删除键值对
    return 0;
}

9.6 unordered_set:基于哈希表实现的无序集合,元素唯一。

#include <iostream>
#include <unordered_set>

int main() {
    std::unordered_set<int> ust = {1, 2, 3, 4, 5};
    ust.insert(6); // 插入元素
    ust.erase(6);  // 删除元素
    return 0;
}

9.7 unordered_map:基于哈希表实现的无序映射,键值对存储,键唯一。

#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> ump = {{1, "one"}, {2, "two"}, {3, "three"}};
    ump[4] = "four"; // 插入键值对
    ump.erase(4);     // 删除键值对
    return 0;
}

十. C++中的IO流

C中的IO流(Input/Output Streams)是一个强大的工具,用于处理程序的输入和输出。这些流可以是文件、控制台、网络套接字等。C标准库提供了多种IO流类,如std::istream(用于输入),std::ostream(用于输出),std::fstream(用于文件IO),std::cin(用于从控制台读取输入),std::cout(用于向控制台输出)等。

以下是一些使用C++ IO流的样例:

10.1控制台输入输出

#include <iostream>

int main() {
    // 输出到控制台
    std::cout << "Hello, World!" << std::endl;

    // 从控制台读取输入
    int number;
    std::cout << "Enter a number: ";
    std::cin >> number;
    std::cout << "You entered: " << number << std::endl;

    return 0;
}

10.2 文件输入输出

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // 写入文件
    std::ofstream outfile("output.txt"); // 创建一个输出文件流对象
    if (outfile.is_open()) {
        outfile << "This is a test." << std::endl;
        outfile.close(); // 关闭文件
    } else {
        std::cerr << "Unable to open file";
    }

    // 读取文件
    std::ifstream infile("output.txt"); // 创建一个输入文件流对象
    std::string content;
    if (infile.is_open()) {
        std::getline(infile, content); // 读取一行内容
        infile.close(); // 关闭文件
        std::cout << "File content: " << content << std::endl;
    } else {
        std::cerr << "Unable to open file";
    }

    return 0;
}

10.3 使用std::stringstream进行字符串处理

#include <iostream>
#include <sstream>
#include <string>

int main() {
    std::string str = "123 456";
    std::stringstream ss(str); // 创建一个stringstream对象,并初始化为str

    int num1, num2;
    char space;

    // 从stringstream中读取数据
    ss >> num1 >> space >> num2;

    std::cout << "num1: " << num1 << ", num2: " << num2 << std::endl;

    return 0;
}

10.4 格式化输出

#include <iostream>
#include <iomanip> // 用于控制格式

int main() {
    double pi = 3.14159265358979323846;

    // 设置输出精度和宽度
    std::cout << std::fixed << std::setprecision(2) << "Value of pi: " << pi << std::endl;

    // 设置填充字符和字段宽度
    std::cout << std::setw(10) << std::setfill('*') << "Right Aligned" << std::endl;
    std::cout << std::setw(10) << std::left << std::setfill('*') << "Left Aligned" << std::endl;

    return 0;
}

以上代码示例演示了如何在C中使用不同类型的IO流,包括控制台输入输出、文件IO、字符串处理以及格式化输出。这些流操作使C能够方便地处理各种输入和输出任务。

十一.C++中的多线程

C11及之后的版本引入了线程库,使得在C中创建和管理多线程变得更加容易和直观。C++的线程库包含在一个名为<thread>的头文件中,并提供了一个std::thread类来表示和管理线程。

以下是C++中多线程的一些基本用法:

11.1 创建线程

使用std::thread类创建一个新线程,并传递给它一个可调用的目标(如函数、函数对象、Lambda表达式或成员函数及对象)。

#include <iostream>
#include <thread>

void print_hello() {
    std::cout << "Hello from thread " << std::this_thread::get_id() << '\n';
}

int main() {
    std::thread t(print_hello); // 创建一个新线程来运行print_hello函数
    t.join(); // 等待线程完成
    return 0;
}

11.2 传递参数给线程函数

你可以通过构造函数将参数传递给线程函数。

#include <iostream>
#include <thread>

void print_number(int num) {
    std::cout << "Number is " << num << " from thread " << std::this_thread::get_id() << '\n';
}

int main() {
    std::thread t(print_number, 42); // 创建一个新线程来运行print_number函数,并传递参数42
    t.join(); // 等待线程完成
    return 0;
}

11.3 分离线程

使用std::thread::detach()方法可以使线程在后台运行,而不需要等待其完成。

#include <iostream>
#include <thread>

void print_hello() {
    std::cout << "Hello from detached thread " << std::this_thread::get_id() << '\n';
}

int main() {
    std::thread t(print_hello);
    t.detach(); // 线程在后台运行,main函数不会等待它完成
    return 0; // 主线程结束,但后台线程可能还在运行
}

11.4 线程ID和当前线程

std::this_thread::get_id()函数返回当前线程的ID。

11.5 线程同步

C++提供了多种机制来实现线程同步,包括互斥量(std::mutex)、条件变量(std::condition_variable)、锁保护(std::lock_guardstd::unique_lock)、原子操作(std::atomic)等。

11.6 线程安全

在多线程编程中,必须确保代码是线程安全的,即多个线程同时访问共享数据时,不会导致数据损坏或不一致的结果。这通常通过使用互斥量、原子操作或同步原语来实现。

11.7 线程局部存储

thread_local关键字可以在C++中声明线程局部存储的变量。这些变量在每个线程中都有自己的副本,因此可以在不同线程中安全地访问和修改它们,而不会互相干扰。

示例:使用互斥量保护共享数据

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

std::mutex mtx; // 全局互斥量
int counter = 0; // 共享数据

void increment() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // 使用锁保护区域
        ++counter;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final counter value is " << counter << '\n';
    return 0;
}

在上面的示例中,我们使用了一个互斥量mtx来保护对共享变量counter的访问,确保两个线程不会同时修改它。