智能指针,对上一篇文章的补充

概念

智能指针是一个对象,它的行为类似于指针,但它可以自动管理指向的对象的生命周期。它是一种抽象数据类型,可以在 C++ 等编程语言中使用,以帮助开发人员更轻松地管理内存资源,避免内存泄漏和其他与内存相关的错误。

智能指针通常具有以下特点:

  • 自动管理内存。智能指针可以自动跟踪其指向的对象的生命周期,并在不需要时自动释放内存。这意味着开发人员不必手动跟踪内存,并且可以更容易地编写可维护的代码。

  • 语法与指针相似。智能指针的使用方式类似于原始指针,因此开发人员可以很容易地将其替换为原始指针,而无需更改大量代码。

  • 可以定制。智能指针的行为可以定制,以满足不同的需求。例如,可以使用引用计数来确定何时释放指针,或者可以使用“独占”指针来防止多个指针引用同一个对象。

常见的智能指针包括:shared_ptrunique_ptrweak_ptr 等。

shared_ptr

简介

shared_ptr C++ 标准模板库(STL)中的一个智能指针。它提供了动态分配对象的共享所有权,这意味着多个 shared_ptr 对象可以拥有和管理同一个对象。这样可以实现高效且安全的资源管理,因为当所有引用它的shared_ptr 对象都被销毁时,对象会被自动删除。

shared_ptr 定义在<memory>头文件中,通常用于管理使用 new 运算符分配的对象。要创建一个 shared_ptr对象,可以使用 std::make_shared 函数,它可以在一步中分配和构造对象:

std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(arg1, arg2, ...);

获得 shared_ptr 对象后,可以使用解引用运算符(*)或箭头运算符(->)来访问它管理的对象,就像使用原始指针一样:

(*ptr).foo();
ptr->bar();

还可以通过复制或赋值现有的 shared_ptr 对象来创建指向同一对象的新 shared_ptr 对象:

std::shared_ptr<MyClass> ptr2 = ptr; // ptr2 和 ptr 现在指向同一个对象

每次创建新的 shared_ptr 时,托管对象的引用计数都会递增,每次销毁或重置 shared_ptr 时,引用计数都会递减。当引用计数达到零时,托管对象会被自动删除。

shared_ptr 还提供了许多其他功能,例如自定义删除器、弱指针和与自定义内存分配器一起使用的能力。

接口及其用途

下面是shared_ptr的所有接口及其用途的代码块和注释:

template<class T> class shared_ptr {
public:
    // 构造函数,创建一个空的shared_ptr
    constexpr shared_ptr() noexcept;

    // 构造函数,创建一个shared_ptr,指向一个新分配的对象
    explicit shared_ptr(T* p);

    // 构造函数,创建一个shared_ptr,共享另一个shared_ptr指向的对象
    shared_ptr(const shared_ptr& r) noexcept;

    // 构造函数,创建一个shared_ptr,共享另一个shared_ptr指向的对象
    template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;

    // 构造函数,创建一个shared_ptr,共享另一个shared_ptr指向的对象,但指向不同的类型
    template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);

    // 构造函数,创建一个shared_ptr,共享另一个shared_ptr指向的对象,但指向不同的类型
    template< class Y, class Deleter >
    shared_ptr( Y* ptr, Deleter d );

    // 构造函数,创建一个shared_ptr,共享另一个shared_ptr指向的对象,但指向不同的类型
    template< class Y, class Deleter, class Alloc >
    shared_ptr( Y* ptr, Deleter d, Alloc alloc );

    // 析构函数,释放对象的所有权,如果引用计数为0则销毁对象
    ~shared_ptr();

    // 赋值操作符,使当前shared_ptr指向r所指向的对象
    shared_ptr& operator=(const shared_ptr& r) noexcept;

    // 赋值操作符,使当前shared_ptr指向r所指向的对象
    template<class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;

    // 重载赋值操作符,使当前shared_ptr指向r所指向的对象
    template<class Y> shared_ptr& operator=(const weak_ptr<Y>& r);

    // 重载箭头操作符,返回指向对象的指针
    T* operator->() const noexcept;

    // 重载解引用操作符,返回指向对象的引用
    T& operator*() const noexcept;

    // 返回指向对象的指针
    T* get() const noexcept;

    // 释放对象的所有权,如果引用计数为0则销毁对象
    void reset() noexcept;

    // 释放对象的所有权,使当前shared_ptr指向p指向的对象
    template<class Y> void reset(Y* p);

    // 释放对象的所有权,使当前shared_ptr指向p指向的对象,并调用自定义删除器d
    template<class Y, class Deleter> void reset(Y* p, Deleter d);

    // 交换当前shared_ptr和另一个shared_ptr的指向
    void swap(shared_ptr& r) noexcept;

    // 返回当前shared_ptr所指向对象的引用计数
    long use_count() const noexcept;

    // 返回当前shared_ptr是否为空
    explicit operator bool() const noexcept;
};

案例

shared_ptr的基本使用

#include <iostream>
#include <memory>

int main() {
    // 创建一个shared_ptr指向一个int类型的对象
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    
    // 输出sharedPtr指向的对象的值
    std::cout << "sharedPtr: " << *sharedPtr << std::endl;
    
    // sharedPtr的引用计数
    std::cout << "sharedPtr use count: " << sharedPtr.use_count() << std::endl;
    
    return 0;
}

shared_ptr的循环引用问题

#include <iostream>
#include <memory>

class B;

class A {
public:
    std::shared_ptr<B> bPtr;
    ~A() { std::cout << "A destroyed" << std::endl; }
};

class B {
public:
    std::shared_ptr<A> aPtr;
    ~B() { std::cout << "B destroyed" << std::endl; }
};

int main() {
    std::shared_ptr<A> aPtr = std::make_shared<A>();
    std::shared_ptr<B> bPtr = std::make_shared<B>();
    aPtr->bPtr = bPtr;
    bPtr->aPtr = aPtr;
    return 0;
}

shared_ptr的自定义删除器

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass(int n) : num(n) {}
    ~MyClass() { std::cout << "MyClass destroyed: " << num << std::endl; }
private:
    int num;
};

void myDeleter(MyClass* ptr) {
    std::cout << "myDeleter called: " << ptr << std::endl;
    delete ptr;
}

int main() {
    std::shared_ptr<MyClass> sharedPtr(new MyClass(42), myDeleter);
    return 0;
}

shared_ptr的数组形式使用

#include <iostream>
#include <memory>

int main() {
    // 创建一个shared_ptr指向一个数组
    std::shared_ptr<int> sharedPtr(new int[5]{1, 2, 3, 4, 5}, [](int* ptr){delete[] ptr;});
    
    // 输出sharedPtr指向的数组的第一个元素的值
    std::cout << "sharedPtr[0]: " << sharedPtr.get()[0] << std::endl;
    
    // sharedPtr的引用计数
    std::cout << "sharedPtr use count: " << sharedPtr.use_count() << std::endl;
    
    return 0;
}

shared_ptr的空指针使用

#include <iostream>
#include <memory>

int main() {
    // 创建一个空的shared_ptr
    std::shared_ptr<int> sharedPtr;
    
    // 判断sharedPtr是否为空指针
    if (!sharedPtr) {
        std::cout << "sharedPtr is null." << std::endl;
    }
    
    // 尝试输出sharedPtr指向的对象的值
    // 由于sharedPtr为空指针,将抛出异常
    std::cout << *sharedPtr << std::endl;
    
    return 0;
}

高阶案例

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

// 自定义删除器类,用于释放一块动态分配的内存
class CustomDeleter {
public:
    void operator()(int* ptr) {
        std::cout << "CustomDeleter: " << *ptr << std::endl;
        delete ptr;
    }
};

// 自定义内存分配器类,用于跟踪已分配的内存大小
class CustomAllocator {
public:
    void* allocate(size_t size) {
        std::lock_guard<std::mutex> lock(mutex_);
        allocated_size_ += size;
        std::cout << "CustomAllocator allocated " << size << " bytes" << std::endl;
        return malloc(size);
    }

    void deallocate(void* ptr, size_t size) {
        std::lock_guard<std::mutex> lock(mutex_);
        allocated_size_ -= size;
        std::cout << "CustomAllocator deallocated " << size << " bytes" << std::endl;
        free(ptr);
    }

    size_t allocated_size() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return allocated_size_;
    }

private:
    std::mutex mutex_;
    size_t allocated_size_ = 0;
};

int main() {
    // 使用自定义删除器创建一个shared_ptr,它指向动态分配的int类型对象1
    std::shared_ptr<int> sp1(new int(1), CustomDeleter());

    // 使用自定义内存分配器创建一个shared_ptr,它指向动态分配的int类型对象2
    CustomAllocator allocator;
    std::shared_ptr<int> sp2(std::allocate_shared<int>(allocator, 2));

    // 用std::make_shared创建一个shared_ptr,它指向动态分配的int类型对象3
    auto sp3 = std::make_shared<int>(3);

    // 创建一个weak_ptr,它指向sp1所指的对象
    std::weak_ptr<int> wp1(sp1);

    // 在一个新的线程中使用weak_ptr,打印它所指向的对象的值
    std::thread t([&wp1]() {
        auto sp = wp1.lock();
        if (sp) {
            std::cout << "value of wp1: " << *sp << std::endl;
        }
        else {
            std::cout << "wp1 is expired" << std::endl;
        }
    });
    t.join();

    // 使用shared_ptr的swap函数,交换sp1和sp3的指向对象
    sp1.swap(sp3);

    // 打印sp1和sp3所指向的对象的值
    std::cout << "value of sp1: " << *sp1 << std::endl;
    std::cout << "value of sp3: " << *sp3 << std::endl;

    // 打印已分配的内存大小
    std::cout << "allocated size: " << allocator.allocated_size() << std::endl;

    return 0;
}

这段代码主要演示了shared_ptr的一些高级用法,包括自定义删除器、自定义内存分配器、weak_ptr等。具体解释如下:

  1. 定义了一个自定义删除器CustomDeleter,用于释放动态分配的int类型对象的内存。

  1. 定义了一个自定义内存分配器CustomAllocator,用于跟踪已分配的内存大小。

  1. 创建了一个shared_ptr sp1,指向动态分配的int类型对象1,使用了自定义删除器CustomDeleter。

  1. 创建了一个shared_ptr sp2,指向动态分配的int类型对象2,使用了自定义内存分配器CustomAllocator。

  1. 创建了一个shared_ptr sp3,指向动态分配的int类型对象3,使用了std::make_shared函数。

  1. 创建了一个weak_ptr wp1,指向sp1所指的对象。

  1. 在一个新的线程中使用weak_ptr wp1,打印它所指向的对象的值。由于这里没有对sp1进行任何修改,因此wp1仍然指向对象1,输出值为1。

  1. 使用shared_ptr的swap函数,交换sp1和sp3的指向对象。此时sp1指向对象3,sp3指向对象1。

  1. 打印sp1和sp3所指向的对象的值。由于步骤8中进行了交换,因此输出值分别为3和1。

  1. 打印自定义内存分配器CustomAllocator已分配的内存大小。

这段代码通过演示shared_ptr的高级用法,可以帮助我们更好地理解shared_ptr的内部机制,并可以根据实际需求自定义删除器、内存分配器等。

unique_ptr

简介

unique_ptr 是一个C++标准库的智能指针类型,其目的是为了方便管理动态分配的资源。

unique_ptr 的创建方式有两种:

  • 使用 make_unique 函数创建一个新对象,并将其传递给一个 unique_ptr。

  • 使用 new 运算符分配一个新对象,并将其传递给一个 unique_ptr

unique_ptr的基本概念包括:

  1. unique_ptr是一个模板类,它的模板参数表示所拥有对象的类型。

  1. unique_ptr是独占式拥有所管理的对象,不能进行复制,,因为这可能导致多个 unique_ptr 拥有同一对象的所有权,只能进行移动,这通常是通过使用 std::move 函数来实现的,即可以将一个 unique_ptr 的所有权转移到另一个 unique_ptr

  1. unique_ptr通过重载运算符->*来提供对所管理对象的访问。

  1. unique_ptr可以通过显式的调用reset()来释放所管理的对象并将指针置空。

  1. unique_ptr还提供了自定义的删除器,用于在释放所管理对象时执行额外的清理操作,例如释放资源或者执行特定的析构函数。

使用unique_ptr可以避免一些常见的动态内存分配和释放的错误,例如内存泄漏和悬垂指针等问题。

使用 unique_ptr 创建对象的方式与普通指针类似,通过 new 操作符动态分配一个对象,并将返回的指针传递给 unique_ptr 的构造函数,例如:

std::unique_ptr<int> p(new int(42));

unique_ptr 超出其作用域时,它所管理的对象也会被自动释放,例如:

{
    std::unique_ptr<int> p(new int(42));
    // use p to work with the pointed-to object
} // p is destroyed here, and the pointed-to int is automatically deleted

由于 unique_ptr 拥有独占性,它不能被复制或赋值给另一个 unique_ptr,可以通过移动构造函数或移动赋值运算符来转移 unique_ptr 的所有权,例如:

std::unique_ptr<int> p1(new int(42));
std::unique_ptr<int> p2 = std::move(p1); // ownership of the pointed-to int is transferred from p1 to p2

使用 unique_ptr 的好处在于,当对象超出其作用域时,不需要手动释放分配的内存,从而避免了内存泄漏的风险。此外,由于 unique_ptr 拥有独占性,可以避免不同指针之间的潜在冲突和竞争条件。

接口及其用途

template<class T, class Deleter = std::default_delete<T>>
class unique_ptr {
public:
    // 构造函数,创建一个空的 unique_ptr 对象
    constexpr unique_ptr() noexcept;

    // 构造函数,使用给定的指针构造一个 unique_ptr 对象
    explicit unique_ptr(T* ptr) noexcept;

    // 构造函数,使用给定的指针和自定义删除器构造一个 unique_ptr 对象
    unique_ptr(T* ptr, typename std::conditional<std::is_reference<Deleter>::value, Deleter, const Deleter&>::type d) noexcept;

    // 构造函数,使用给定的指针和移动语义的自定义删除器构造一个 unique_ptr 对象
    unique_ptr(T* ptr, std::remove_reference_t<Deleter>&& d) noexcept;

    // 构造函数,创建一个空的 unique_ptr 对象
    unique_ptr(std::nullptr_t) noexcept;

    // 析构函数,释放 unique_ptr 持有的指针资源
    ~unique_ptr();

    // 禁止拷贝构造函数,保证只有一个 unique_ptr 对象可以持有资源
    unique_ptr(const unique_ptr&) = delete;

    // 移动构造函数,从另一个 unique_ptr 对象中接管资源
    unique_ptr(unique_ptr&& u) noexcept;

    // 重载赋值运算符,从另一个 unique_ptr 对象中接管资源
    unique_ptr& operator=(unique_ptr&& u) noexcept;

    // 释放 unique_ptr 对象持有的指针资源,并置空指针
    T* release() noexcept;

    // 重置 unique_ptr 持有的指针资源
    void reset(T* ptr = nullptr) noexcept;

    // 交换两个 unique_ptr 对象的指针资源
    void swap(unique_ptr& u) noexcept;

    // 获取 unique_ptr 持有的指针资源
    T* get() const noexcept;

    // 重载取值运算符,获取 unique_ptr 持有的指针资源
    T& operator*() const;

    // 重载箭头运算符,获取 unique_ptr 持有的指针资源
    T* operator->() const noexcept;

    // 获取自定义删除器的引用
    typename std::add_lvalue_reference<Deleter>::type get_deleter() noexcept;

    // 获取自定义删除器的常量引用
    typename std::add_lvalue_reference<const Deleter>::type get_deleter() const noexcept;

    // 判断 unique_ptr 是否持有资源
    explicit operator bool() const noexcept;
};

案例

简单的unique_ptr使用案例

#include <iostream>
#include <memory>

int main() {
    // 创建一个unique_ptr并将其初始化为指向一个整数变量的地址
    std::unique_ptr<int> ptr(new int(42));

    // 输出指针指向的值
    std::cout << "The value pointed to by ptr is: " << *ptr << std::endl;

    // 释放指针所指向的内存
    ptr.reset();

    return 0;
}

unique_ptr在函数间传递

#include <iostream>
#include <memory>

void foo(std::unique_ptr<int>& ptr) {
    *ptr = 42;
}

int main() {
    // 创建一个unique_ptr并将其初始化为指向一个整数变量的地址
    std::unique_ptr<int> ptr(new int(0));

    // 输出指针指向的值
    std::cout << "The value pointed to by ptr is: " << *ptr << std::endl;

    // 调用函数并传递unique_ptr
    foo(ptr);

    // 输出指针指向的值
    std::cout << "The value pointed to by ptr is now: " << *ptr << std::endl;

    // 释放指针所指向的内存
    ptr.reset();

    return 0;
}

unique_ptr管理动态数组

#include <iostream>
#include <memory>

int main() {
    // 创建一个unique_ptr并将其初始化为指向动态分配的int数组的第一个元素的地址
    std::unique_ptr<int[]> arr(new int[3]);

    // 给数组赋值
    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;

    // 输出数组的值
    std::cout << "The array contains: " << arr[0] << ", " << arr[1] << ", " << arr[2] << std::endl;

    // 释放指针所指向的内存
    arr.reset();

    return 0;
}

使用unique_ptr管理动态分配的对象数组

#include <iostream>
#include <memory>

int main() {
    // 创建unique_ptr对象,用于管理int类型的动态数组
    std::unique_ptr<int[]> nums(new int[5]{1, 2, 3, 4, 5});

    // 访问数组中的元素
    for (int i = 0; i < 5; i++) {
        std::cout << nums[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

使用unique_ptr作为函数返回值

#include <iostream>
#include <memory>

// 返回一个unique_ptr对象,管理动态分配的字符串
std::unique_ptr<std::string> getString() {
    std::unique_ptr<std::string> str(new std::string("Hello World!"));
    return str;
}

int main() {
    // 获取函数返回的unique_ptr对象
    std::unique_ptr<std::string> result = getString();

    // 输出字符串
    std::cout << *result << std::endl;

    return 0;
}

使用unique_ptr实现简单的RAII机制

#include <iostream>
#include <memory>

class File {
public:
    // 构造函数,打开文件
    File(const char* filename) : fp_(fopen(filename, "r")) {
        if (fp_ == nullptr) {
            throw std::runtime_error("failed to open file");
        }
    }

    // 析构函数,关闭文件
    ~File() {
        if (fp_ != nullptr) {
            fclose(fp_);
        }
    }

    // 读取文件内容并打印
    void read() {
        char buffer[1024];
        while (fgets(buffer, 1024, fp_)) {
            std::cout << buffer;
        }
    }

private:
    FILE* fp_;  // 文件指针
};

int main() {
    try {
        // 使用unique_ptr管理动态分配的File对象
        std::unique_ptr<File> file(new File("test.txt"));

        // 读取文件内容并打印
        file->read();
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

该案例演示了使用unique_ptr实现简单的RAII机制,将资源的获取和释放封装到对象的构造和析构函数中,方便进行资源管理。在该案例中,通过unique_ptr来管理动态分配的File对象,文件的打开和关闭都封装在File类的构造函数和析构函数中,使用unique_ptr对象时不需要手动释放资源。

高阶案例

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

// 声明一个Animal类
class Animal {
public:
    virtual void speak() = 0;
};

// Dog类继承自Animal类
class Dog : public Animal {
public:
    virtual void speak() override {
        std::cout << "Woof!" << std::endl;
    }
};

// Cat类继承自Animal类
class Cat : public Animal {
public:
    virtual void speak() override {
        std::cout << "Meow!" << std::endl;
    }
};

// 自定义删除器类,用于释放一块动态分配的内存
class CustomDeleter {
public:
    void operator()(Animal* ptr) {
        std::cout << "CustomDeleter: " << std::endl;
        delete ptr;
    }
};

int main() {
    // 创建一个vector,存储指向Animal对象的unique_ptr
    std::vector<std::unique_ptr<Animal>> animals;

    // 创建一个unique_ptr,它指向动态分配的Dog类型对象
    auto dog = std::make_unique<Dog>();

    // 将指向Dog对象的unique_ptr插入到vector中
    animals.push_back(std::move(dog));

    // 创建一个unique_ptr,它指向动态分配的Cat类型对象
    auto cat = std::make_unique<Cat>();

    // 将指向Cat对象的unique_ptr插入到vector中
    animals.push_back(std::move(cat));

    // 遍历vector中的所有unique_ptr,并调用它们的speak函数
    for (const auto& animal : animals) {
        animal->speak();
    }

    // 创建一个指向Animal对象的unique_ptr,它使用自定义删除器释放内存
    std::unique_ptr<Animal, CustomDeleter> animalWithCustomDeleter(new Cat(), CustomDeleter());

    // 创建一个指向Animal对象的unique_ptr,它使用默认删除器释放内存
    std::unique_ptr<Animal> animalWithDefaultDeleter(new Dog());

    // 打印指向Animal对象的unique_ptr所指向的类型名
    std::cout << "type of animalWithCustomDeleter: " << typeid(*animalWithCustomDeleter).name() << std::endl;
    std::cout << "type of animalWithDefaultDeleter: " << typeid(*animalWithDefaultDeleter).name() << std::endl;

    return 0;
}

这段代码定义了Animal、Dog和Cat三个类。Animal是一个抽象类,其中包含一个纯虚函数speak。Dog和Cat分别是Animal类的子类,它们分别实现了speak函数。

这段代码还定义了一个CustomDeleter类,用于释放动态分配的内存。在main函数中,通过std::vector存储了指向Animal对象的unique_ptr,这些unique_ptr指向了Dog和Cat对象。

在main函数中,首先通过std::make_unique创建了两个unique_ptr,分别指向Dog和Cat类型的对象。然后将这些unique_ptr插入到std::vector中。

随后,使用一个for循环遍历vector中的所有unique_ptr,并调用它们的speak函数,输出Dog和Cat发出的声音。

接下来,使用std::unique_ptr创建了两个unique_ptr,一个使用自定义删除器CustomDeleter,另一个使用默认删除器。使用typeid运算符打印出这两个unique_ptr所指向的对象类型名。

总的来说,这段代码演示了如何使用unique_ptr来管理动态分配的对象,以及如何使用自定义删除器。

weak_ptr

简介

weak_ptr 是 C++11 标准中智能指针的一种,它是一种弱引用(weak reference),与 shared_ptr 相似,也是用于管理堆上的动态内存。但是 weak_ptr 不会增加所管理对象的引用计数,也不负责其生命周期的管理。当 shared_ptr 所管理的对象已经不存在时,使用 weak_ptr 访问该对象会返回一个空指针,避免了 shared_ptr 的循环引用问题。

通常情况下,weak_ptr 用于解决 shared_ptr 循环引用问题,可以将一个 weak_ptr 绑定到一个 shared_ptr 上,此时 weak_ptr 不会改变对象的引用计数,当所管理的对象被销毁时,weak_ptr 会自动失效,此时再使用它访问所管理的对象会返回一个空指针。

weak_ptr 通过 lock() 函数来获取指向 shared_ptr 所管理对象的指针,但在获取前需要判断 weak_ptr 是否已经失效,如果已经失效,lock() 函数返回一个空指针。

需要注意的是,weak_ptr 不能直接访问所管理的对象,因为它不会增加引用计数,当 shared_ptr 所管理的对象被销毁时,weak_ptr 指向的对象也会被销毁,访问该对象会导致未定义行为。

接口及其用途

下面是 weak_ptr 类的所有接口及其用途:

#include <memory>

template <typename T>
class weak_ptr {
public:
    // 构造函数,创建一个空的 weak_ptr 对象
    weak_ptr() noexcept;

    // 拷贝构造函数,创建一个与其他 weak_ptr 对象共享同一个控制块的新 weak_ptr 对象
    weak_ptr(const weak_ptr& other) noexcept;

    // 移动构造函数,创建一个新的 weak_ptr 对象,并将其接管给定的其他 weak_ptr 对象的资源
    weak_ptr(weak_ptr&& other) noexcept;

    // 从 shared_ptr 对象创建 weak_ptr 对象
    template <typename Y>
    weak_ptr(const std::shared_ptr<Y>& other) noexcept;

    // 从 unique_ptr 对象创建 weak_ptr 对象
    template <typename Y, typename Deleter>
    weak_ptr(const std::unique_ptr<Y, Deleter>& other) noexcept;

    // 析构函数,释放所有与此 weak_ptr 对象相关的资源
    ~weak_ptr();

    // 拷贝赋值运算符,与另一个 weak_ptr 对象共享同一个控制块
    weak_ptr& operator=(const weak_ptr& other) noexcept;

    // 移动赋值运算符,接管另一个 weak_ptr 对象的资源
    weak_ptr& operator=(weak_ptr&& other) noexcept;

    // 将 weak_ptr 对象与 shared_ptr 对象进行比较
    template <typename Y>
    bool operator==(const std::shared_ptr<Y>& other) const noexcept;

    // 将 weak_ptr 对象与 shared_ptr 对象进行比较
    template <typename Y>
    bool operator!=(const std::shared_ptr<Y>& other) const noexcept;

    // 将 weak_ptr 对象与其他 weak_ptr 对象进行比较
    bool operator==(const weak_ptr& other) const noexcept;

    // 将 weak_ptr 对象与其他 weak_ptr 对象进行比较
    bool operator!=(const weak_ptr& other) const noexcept;

    // 重置 weak_ptr 对象,释放所有与该对象相关的资源
    void reset() noexcept;

    // 检查 weak_ptr 对象是否为空
    bool expired() const noexcept;

    // 尝试将 weak_ptr 对象转换为 shared_ptr 对象
    std::shared_ptr<T> lock() const noexcept;

    // 返回与此 weak_ptr 对象相关的 shared_ptr 对象的数量
    long use_count() const noexcept;
};

案例

简单使用:创建一个shared_ptr对象并使用weak_ptr来访问它

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> mySharedPtr = std::make_shared<int>(42);
    std::weak_ptr<int> myWeakPtr = mySharedPtr;

    if (auto sharedPtr = myWeakPtr.lock()) {
        std::cout << "The value of the shared_ptr is: " << *sharedPtr << std::endl;
    } else {
        std::cout << "The shared_ptr is expired!" << std::endl;
    }

    return 0;
}

使用weak_ptr来打破shared_ptr的循环引用

#include <iostream>
#include <memory>

class Node {
public:
    std::weak_ptr<Node> next;

    Node(int value) : value(value) {}

    ~Node() {
        std::cout << "Node with value " << value << " destroyed!" << std::endl;
    }

private:
    int value;
};

int main() {
    std::shared_ptr<Node> node1 = std::make_shared<Node>(1);
    std::shared_ptr<Node> node2 = std::make_shared<Node>(2);

    // create a circular reference between node1 and node2
    node1->next = node2;
    node2->next = node1;

    // break the circular reference using weak_ptr
    node1->next = std::weak_ptr<Node>(node2);
    node2->next = std::weak_ptr<Node>(node1);

    return 0;
}

使用weak_ptr来避免野指针问题

#include <iostream>
#include <memory>

int main() {
    std::weak_ptr<int> myWeakPtr;

    {
        std::shared_ptr<int> mySharedPtr = std::make_shared<int>(42);
        myWeakPtr = mySharedPtr;

        std::cout << "The value of the shared_ptr is: " << *myWeakPtr.lock() << std::endl;
    }

    if (myWeakPtr.expired()) {
        std::cout << "The shared_ptr has been deleted!" << std::endl;
    } else {
        std::cout << "The shared_ptr still exists!" << std::endl;
    }

    return 0;
}

将weak_ptr用作缓存

#include <iostream>
#include <map>
#include <memory>

int main() {
    std::map<int, std::weak_ptr<int>> cache;

    // retrieve a value from the cache
    int key = 42;
    if (auto ptr = cache[key].lock()) {
        std::cout << "Cache hit: " << *ptr << std::endl;
    } else {
        std::cout << "Cache miss!" << std::endl;

        // calculate the value and store it in the cache
        std::shared_ptr<int> value = std::make_shared<int>(key * 2);
        cache[key] = value;

        std::cout << "The calculated value is: " << *value << std::endl;
    }

    return 0;
}

使用weak_ptr在多个线程之间传递数据

#include <iostream>
#include <memory>
#include <thread>
#include <vector>

void worker(std::weak_ptr<int> wp) {
    auto sp = wp.lock();
    if (sp) {
        std::cout << "Worker thread: Got value " << *sp << " from shared_ptr.\n";
        *sp = *sp * 2;
        std::cout << "Worker thread: Set value to " << *sp << ".\n";
    } else {
        std::cout << "Worker thread: Failed to get shared_ptr.\n";
    }
}

int main() {
    auto sp = std::make_shared<int>(42);
    std::weak_ptr<int> wp = sp;

    std::vector<std::thread> threads;

    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(worker, wp);
    }

    for (auto& t : threads) {
        t.join();
    }

    std::cout << "Main thread: Final value is " << *sp << ".\n";

    return 0;
}

高阶案例

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

class Person;

// 定义Car类,其中包含一个指向Person对象的weak_ptr成员变量
class Car {
private:
    std::weak_ptr<Person> owner_;

public:
    Car() = default;
    Car(const std::shared_ptr<Person>& owner) : owner_(owner) {}

    // 打印出拥有该Car对象的Person对象的名字
    void getOwnerName() {
        if (auto owner = owner_.lock()) {
            std::cout << "The owner of the car is " << owner->getName() << std::endl;
        } else {
            std::cout << "The car has no owner." << std::endl;
        }
    }
};

// 定义Person类,其中包含一个vector,其中存储指向Car对象的shared_ptr
class Person {
private:
    std::string name_;
    std::vector<std::shared_ptr<Car>> cars_;

public:
    Person(const std::string& name) : name_(name) {}

    std::string getName() const {
        return name_;
    }

    // 购买一辆车,并将该车的shared_ptr插入到cars_ vector中
    void buyCar(const std::shared_ptr<Car>& car) {
        cars_.push_back(car);
        car->getOwnerName();
    }

    // 出售一辆车,从cars_ vector中删除该车的shared_ptr
    void sellCar(const std::shared_ptr<Car>& car) {
        auto it = std::find(cars_.begin(), cars_.end(), car);
        if (it != cars_.end()) {
            cars_.erase(it);
            std::cout << name_ << " just sold a car." << std::endl;
        }
    }
};

int main() {
    // 创建两个Person对象
    auto person1 = std::make_shared<Person>("John");
    auto person2 = std::make_shared<Person>("Jane");

    // 创建两辆Car对象,其中一辆属于person1,另一辆没有owner
    auto car1 = std::make_shared<Car>(person1);
    auto car2 = std::make_shared<Car>();

    // person1购买car1
    person1->buyCar(car1);

    // person2购买car2
    person2->buyCar(car2);

    // person1将car1卖给person2
    person1->sellCar(car1);
    person2->buyCar(car1); // person2购买car1

    return 0;
}

这段代码实现了一个汽车和人之间的关系管理系统,通过使用std::shared_ptr和std::weak_ptr实现了汽车和人之间的松耦合。

在该代码中,Car类包含一个指向Person对象的std::weak_ptr成员变量owner_,表示该车的所有者。Person类包含一个vector,其中存储指向Car对象的std::shared_ptr。Person类还定义了购买一辆车并将该车的std::shared_ptr插入到cars_ vector中的方法buyCar,以及出售一辆车并从cars_ vector中删除该车的std::shared_ptr的方法sellCar。

在main函数中,创建了两个Person对象person1和person2,以及两辆Car对象car1和car2。person1购买了car1,person2购买了car2。然后person1将car1卖给了person2。在这个过程中,std::weak_ptr确保Car对象可以访问其所有者,而std::shared_ptr则确保Person对象在它所拥有的Car对象被销毁之前保持存活。

猜你喜欢

转载自blog.csdn.net/qq_42815643/article/details/129640754