MST

星途 面试题库

面试题:C++ 智能指针实现原理及自定义智能指针

深入阐述 std::shared_ptr 的引用计数实现原理,包括引用计数的存储方式、线程安全性等方面。然后,请基于 C++ 实现一个简易版的类似 std::shared_ptr 的自定义智能指针,要求具备基本的引用计数管理和内存释放功能。
32.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

std::shared_ptr 引用计数实现原理

  1. 引用计数的存储方式
    • std::shared_ptr 通常将引用计数存储在一个控制块(control block)中。这个控制块除了包含引用计数外,还可能包含指向被管理对象的指针以及其他相关信息,比如弱引用计数(用于 std::weak_ptr)。
    • 当创建一个 std::shared_ptr 对象指向某个动态分配的对象时,会同时创建一个控制块,并将引用计数初始化为 1。例如:
    int* ptr = new int(42);
    std::shared_ptr<int> sp(ptr);
    
    这里 sp 指向 ptr,同时创建的控制块中的引用计数为 1。
    • 当进行赋值(operator=)或拷贝构造时,引用计数会增加。例如:
    std::shared_ptr<int> sp2(sp);
    
    此时 sp2sp 共享同一个控制块,控制块中的引用计数加 1 变为 2。
    • std::shared_ptr 对象析构时,引用计数会减 1。如果引用计数减为 0,控制块会释放被管理的对象以及控制块自身。
  2. 线程安全性
    • std::shared_ptr 本身的多线程操作是线程安全的。也就是说,多个线程可以同时对不同的 std::shared_ptr 对象进行拷贝、赋值、析构等操作,不会出现数据竞争。
    • 然而,对于指向的同一个对象的 std::shared_ptr 之间的操作,比如多个线程同时通过 std::shared_ptr 访问和修改同一个对象的成员,这仍然需要额外的同步机制(如互斥锁)来保证线程安全。
    • 在多线程环境下,引用计数的增减操作通常是原子操作。例如,当一个线程增加引用计数,另一个线程减少引用计数时,不会出现中间状态导致引用计数错误。C++ 标准库通过使用原子类型(如 std::atomic<int>)来实现引用计数的原子操作,从而保证线程安全。

简易版类似 std::shared_ptr 的自定义智能指针实现

#include <iostream>
#include <atomic>

template <typename T>
class MySharedPtr {
private:
    T* ptr;
    std::atomic<int>* refCount;

public:
    // 构造函数
    MySharedPtr(T* p = nullptr) : ptr(p) {
        if (ptr) {
            refCount = new std::atomic<int>(1);
        } else {
            refCount = new std::atomic<int>(0);
        }
    }

    // 拷贝构造函数
    MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), refCount(other.refCount) {
        if (ptr) {
            (*refCount)++;
        }
    }

    // 移动构造函数
    MySharedPtr(MySharedPtr&& other) noexcept : ptr(other.ptr), refCount(other.refCount) {
        other.ptr = nullptr;
        other.refCount = nullptr;
    }

    // 赋值运算符重载
    MySharedPtr& operator=(const MySharedPtr& other) {
        if (this == &other) {
            return *this;
        }
        if (ptr && --(*refCount) == 0) {
            delete ptr;
            delete refCount;
        }
        ptr = other.ptr;
        refCount = other.refCount;
        if (ptr) {
            (*refCount)++;
        }
        return *this;
    }

    // 移动赋值运算符重载
    MySharedPtr& operator=(MySharedPtr&& other) noexcept {
        if (this == &other) {
            return *this;
        }
        if (ptr && --(*refCount) == 0) {
            delete ptr;
            delete refCount;
        }
        ptr = other.ptr;
        refCount = other.refCount;
        other.ptr = nullptr;
        other.refCount = nullptr;
        return *this;
    }

    // 析构函数
    ~MySharedPtr() {
        if (ptr && --(*refCount) == 0) {
            delete ptr;
            delete refCount;
        }
    }

    // 获取指向的对象
    T* get() const {
        return ptr;
    }

    // 获取引用计数
    int use_count() const {
        return *refCount;
    }

    // 解引用操作符重载
    T& operator*() const {
        return *ptr;
    }

    // 箭头操作符重载
    T* operator->() const {
        return ptr;
    }
};