MST

星途 面试题库

面试题:C++ 常指针与指向常变量的指针在复杂数据结构与多线程环境中的应用及陷阱

在一个多线程的 C++ 程序中,有一个复杂的数据结构,例如 `std::vector<std::unique_ptr<MyClass>>`,其中 `MyClass` 是一个自定义类。现在要通过常指针和指向常变量的指针来访问和操作这个数据结构中的元素。请描述可能会遇到的线程安全问题、内存管理问题以及如何利用常指针和指向常变量的指针的特性来保证数据的一致性和安全性,同时给出关键代码示例并详细解释。
45.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

线程安全问题

  1. 数据竞争:当多个线程同时访问和修改 std::vector<std::unique_ptr<MyClass>> 时,可能会发生数据竞争。例如,一个线程在读取元素,另一个线程在删除或插入元素,这可能导致未定义行为。
  2. 竞态条件:如果对数据结构的操作依赖于特定的执行顺序,例如先检查元素是否存在再进行操作,多个线程同时执行这些操作可能导致竞态条件。

内存管理问题

  1. 悬空指针:如果一个线程释放了 MyClass 对象的内存(通过 std::unique_ptr 析构),而另一个线程仍持有指向该对象的指针,就会产生悬空指针。
  2. 双重释放:虽然 std::unique_ptr 本身可以避免双重释放,但在多线程环境下,如果不小心手动释放了内存,然后 std::unique_ptr 又进行析构,就会导致双重释放。

利用常指针和指向常变量的指针特性保证安全

  1. 常指针(MyClass* const:常指针本身不能被重新赋值,但指向的对象可以被修改。这在多线程环境下可以确保指针不会被意外修改,但不能防止对象内容被并发修改。
  2. 指向常变量的指针(const MyClass*:指向常变量的指针不能修改所指向对象的内容。这在多线程环境下可以保证只读操作的线程安全,只要没有线程进行写操作。

关键代码示例

#include <iostream>
#include <vector>
#include <memory>
#include <mutex>

class MyClass {
public:
    int value;
    MyClass(int v) : value(v) {}
};

std::vector<std::unique_ptr<MyClass>> data;
std::mutex dataMutex;

// 线程安全的只读访问
void readData() {
    std::lock_guard<std::mutex> lock(dataMutex);
    for (const auto& ptr : data) {
        const MyClass* const obj = ptr.get();
        std::cout << "Read value: " << obj->value << std::endl;
    }
}

// 线程安全的写访问
void writeData() {
    std::lock_guard<std::mutex> lock(dataMutex);
    data.emplace_back(std::make_unique<MyClass>(42));
}

代码解释

  1. std::mutex dataMutex:用于保护 data 数据结构,防止多线程同时访问和修改。
  2. readData 函数:使用 std::lock_guard 来锁定互斥锁,确保在读取数据时不会有其他线程修改数据。const MyClass* const obj = ptr.get(); 获取指向 MyClass 对象的常指针,保证只读访问。
  3. writeData 函数:同样使用 std::lock_guard 锁定互斥锁,确保在插入新元素时不会有其他线程干扰。

通过使用互斥锁和合理利用常指针和指向常变量的指针特性,可以有效保证多线程环境下 std::vector<std::unique_ptr<MyClass>> 数据结构的一致性和安全性。