面试题答案
一键面试std::unique_ptr
- 工作原理:
std::unique_ptr
采用独占式所有权模型。它持有对动态分配对象的唯一所有权,当std::unique_ptr
本身被销毁(例如离开其作用域)时,它所指向的对象会自动被释放。- 它不能被拷贝,只能被移动。移动语义使得
std::unique_ptr
可以在不同的作用域之间转移所有权。例如:
std::unique_ptr<int> ptr1(new int(42)); std::unique_ptr<int> ptr2 = std::move(ptr1); // ptr1 失去所有权,ptr2 获得所有权
- 适用场景:
- 当你希望某个对象在某个特定的作用域内存在,并且该对象的所有权不应被共享时,使用
std::unique_ptr
。比如一个函数内部创建的临时对象,不需要在函数外部共享其所有权。
- 当你希望某个对象在某个特定的作用域内存在,并且该对象的所有权不应被共享时,使用
std::shared_ptr
- 工作原理:
std::shared_ptr
采用引用计数的方式来管理动态分配的内存。多个std::shared_ptr
可以指向同一个对象,对象的引用计数会记录有多少个std::shared_ptr
指向它。- 当一个
std::shared_ptr
被创建并指向一个新对象时,引用计数初始化为1。每当有新的std::shared_ptr
指向该对象(例如通过拷贝构造函数或赋值操作符),引用计数加1。当一个std::shared_ptr
被销毁(离开作用域或被显式重置),引用计数减1。当引用计数降为0时,对象的内存会被自动释放。 - 例如:
std::shared_ptr<int> ptr1(new int(42)); std::shared_ptr<int> ptr2 = ptr1; // 引用计数从1变为2 ptr1.reset(); // 引用计数从2变为1 ptr2.reset(); // 引用计数从1变为0,对象内存被释放
- 适用场景:
- 当需要在多个地方共享对象的所有权时,使用
std::shared_ptr
。比如在多个函数或模块之间需要访问同一个对象的场景。
- 当需要在多个地方共享对象的所有权时,使用
std::weak_ptr
- 工作原理:
std::weak_ptr
是一种弱引用,它指向由std::shared_ptr
管理的对象,但不会增加对象的引用计数。它主要用于解决std::shared_ptr
可能出现的循环引用问题。std::weak_ptr
可以通过lock()
方法尝试获取一个std::shared_ptr
。如果对象仍然存在(即对应的std::shared_ptr
的引用计数不为0),lock()
方法会返回一个有效的std::shared_ptr
,否则返回一个空的std::shared_ptr
。- 例如:
std::shared_ptr<int> ptr1(new int(42)); std::weak_ptr<int> weakPtr = ptr1; std::shared_ptr<int> ptr3 = weakPtr.lock(); // ptr3 有效,指向同一个对象 ptr1.reset(); ptr3 = weakPtr.lock(); // ptr3 为空,对象已被释放
- 适用场景:
- 当存在可能的循环引用问题时,使用
std::weak_ptr
打破循环。比如在双向链表等数据结构中,一个节点持有指向另一个节点的std::weak_ptr
,以避免循环引用导致内存泄漏。
- 当存在可能的循环引用问题时,使用
多线程环境下的问题及解决
- 问题:
- 引用计数竞争:在多线程环境下,多个线程同时操作
std::shared_ptr
可能导致引用计数的竞争条件。例如,一个线程增加引用计数,而另一个线程同时减少引用计数,可能导致未定义行为。 - 对象访问竞争:多个线程可能同时通过
std::shared_ptr
访问对象,可能导致数据竞争问题,特别是在对象的成员函数不是线程安全的情况下。
- 引用计数竞争:在多线程环境下,多个线程同时操作
- 解决方法:
- 使用互斥锁:可以使用
std::mutex
来保护对std::shared_ptr
的操作。例如,在修改std::shared_ptr
(如赋值、重置等)之前加锁,操作完成后解锁。
std::mutex sharedPtrMutex; std::shared_ptr<int> sharedData; void threadFunction() { std::lock_guard<std::mutex> lock(sharedPtrMutex); // 安全地操作 sharedData sharedData.reset(new int(42)); }
- 使用
std::atomic_shared_ptr
(C++17 及以后):std::atomic_shared_ptr
提供了原子操作语义,使得对std::shared_ptr
的某些操作(如赋值、比较和交换等)是线程安全的,无需手动加锁。
std::atomic_shared_ptr<int> atomicSharedData; void threadFunction() { atomicSharedData.store(std::make_shared<int>(42)); }
- 对象自身线程安全设计:确保对象的成员函数是线程安全的,或者在访问对象成员时使用合适的同步机制,以避免数据竞争。
- 使用互斥锁:可以使用