MST

星途 面试题库

面试题:C++ 中虚函数在多线程环境下可能出现的安全问题及解决方法

在 C++ 中,当涉及多线程调用虚函数时,可能会出现哪些安全问题?请至少列举两个,并阐述对应的解决办法。
16.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 虚函数表指针的竞争问题
    • 问题阐述:多线程同时访问虚函数表指针时可能导致数据竞争。例如,在一个线程中对象的虚函数表指针正在被修改(比如对象的动态类型发生改变),而另一个线程同时通过该对象调用虚函数,此时可能读取到未完全更新的虚函数表指针,导致程序崩溃或未定义行为。
    • 解决办法:可以使用互斥锁(std::mutex)来保护对虚函数表指针可能产生修改的操作。在修改虚函数表指针之前,锁定互斥锁,修改完成后解锁。例如:
#include <iostream>
#include <mutex>
class Base {
public:
    virtual void func() { std::cout << "Base::func" << std::endl; }
};
class Derived : public Base {
public:
    void func() override { std::cout << "Derived::func" << std::endl; }
};
std::mutex vptrMutex;
void threadFunction(Base* obj) {
    vptrMutex.lock();
    // 这里可以安全地对obj的动态类型进行修改等操作
    vptrMutex.unlock();
    obj->func();
}
  1. 对象生命周期管理问题
    • 问题阐述:一个线程可能在另一个线程正在调用虚函数时销毁对象。例如,一个线程调用对象的虚函数过程中,另一个线程将对象删除,这会导致悬空指针访问,引起未定义行为。
    • 解决办法:使用智能指针(如std::shared_ptr)来管理对象的生命周期。std::shared_ptr使用引用计数,只有当引用计数为0时对象才会被销毁。这样,在对象的虚函数调用期间,只要有std::shared_ptr持有对象,对象就不会被意外销毁。例如:
#include <iostream>
#include <memory>
class Base {
public:
    virtual void func() { std::cout << "Base::func" << std::endl; }
};
class Derived : public Base {
public:
    void func() override { std::cout << "Derived::func" << std::endl; }
};
void threadFunction(std::shared_ptr<Base> obj) {
    obj->func();
}
  1. 数据一致性问题
    • 问题阐述:虚函数可能访问和修改对象的成员变量。当多线程调用虚函数时,如果这些成员变量没有适当的同步机制,可能导致数据不一致。例如,一个线程调用虚函数对成员变量进行累加操作,另一个线程同时调用虚函数读取该成员变量,可能得到不正确的值。
    • 解决办法:对于涉及的成员变量,使用同步机制,如互斥锁、读写锁(std::shared_mutex)等。如果读操作多写操作少,可以使用读写锁,允许多个线程同时读,但写操作需要独占锁。例如:
#include <iostream>
#include <mutex>
#include <shared_mutex>
class Base {
public:
    std::shared_mutex dataMutex;
    int data;
    virtual void func() {
        std::shared_lock<std::shared_mutex> lock(dataMutex);
        // 这里安全地读取data
        std::cout << "Data value: " << data << std::endl;
    }
    void modifyData(int newData) {
        std::unique_lock<std::shared_mutex> lock(dataMutex);
        data = newData;
    }
};