Cherno的Cpp教程笔记022:C++的智能指针

Cherno的Cpp教程笔记02



在这里插入图片描述
在这里插入图片描述在这里插入图片描述


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

uinque_ptr

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分享指针

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CG

template <typename T>
class miniunique_ptr
{
public:
    // 构造函数
    explicit miniunique_ptr(T *p = nullptr) : pointer(p) {}

    // 禁用左值构造
    template <typename U>
    miniunique_ptr(miniunique_ptr<U> &) = delete;
    // 允许右值构造
    template <typename U>
    miniunique_ptr(miniunique_ptr<U> &&p) : pointer(p.release()) {}
    // 析构函数
    ~miniunique_ptr()
    {
        delete pointer;
    }
    // 模拟-> * 符号
    T *operator->() const
    {
        return pointer;
    }
    T &operator*() const
    {
        return *pointer;
    }
    // 危险操作 返回原始指针
    T *get() const
    {
        return pointer;
    }
    // 放弃智能指针管理,并没有销毁对象
    T *release()
    {
        T *old_ptr = pointer;
        pointer = nullptr;
        return old_ptr;
    }
    // 控制另一个对象,销毁之前保存的对象 注意可能会出现自己重新控制自己的问题
    void reset(T *p = nullptr)
    {
        if (p != pointer)
        {
            delete pointer;
            pointer = p;
        }
    }
    // 禁止左值赋值
    template <typename U>
    miniunique_ptr<T> &operator=(miniunique_ptr<U> &rp) = delete;
    // 允许右值赋值
    // 重写赋值操作符,这里是于unique_ptr不同的地方,额外再加入一个U是为了增加弹性
    // 等于操作符会释放右操作数到左操作数
    template <typename U>
    miniunique_ptr<T> &operator=(miniunique_ptr<U> &&rp)
    {
        if (this->pointer != rp.pointer)
        {
            reset(rp.release());
        }
        return *this;
    }

private:
    T *pointer;


};

move

std::move 是 C++ 标准库中的一个函数模板,位于 <utility> 头文件中。它用于将对象的所有权从一个对象(即源对象)转移到另一个对象(即目标对象)。

在实际使用中,std::move 将对象的状态标记为“可移动(movable)”,从而允许编译器执行移动语义的优化。它并不实际迁移或复制对象的数据,而只是将对象标记为可用于移动操作的临时状态。

std::move 的作用是将对象的右值引用返回给调用者,使调用者可以使用该右值引用来执行移动或转移操作。通过将右值引用传递给相关的函数或构造函数,可以实现资源的有效转移和避免不必要的数据复制。

以下是 std::move 的示例用法:

#include <iostream>
#include <utility>

int main() {
    int x = 10;
    int y = std::move(x);  // 将 x 的所有权转移到 y

    std::cout << "x: " << x << std::endl;  // 输出未定义的值,因为 x 的所有权已经转移
    std::cout << "y: " << y << std::endl;  // 输出 10,因为 y 获取了 x 的值

    return 0;
}

在示例中,我们使用 std::move 将变量 x 的所有权转移到变量 y,而不是执行数据的实际复制。通过这种方式,我们可以高效地将资源所有权转移到新的对象,避免不必要的开销。

请注意,使用 std::move 后,源对象应被视为处于有效但未定义的状态,不应再对其进行访问。移动后的目标对象将具有有效和定义明确的状态。

unique_ptr与move:(move(x),其目的是将一个左值引用 x 转换为右值引用)

#include <iostream>
#include <memory>

class Tmp {
public:
    Tmp(){};
    Tmp(int a): __a(a){};
    ~Tmp(){};
    virtual void print__a() { std::cout << "value = " << __a << std::endl; }

private:
    int __a;
};

int main() {
    std::unique_ptr<Tmp> up1(new Tmp(5));
    std::unique_ptr<Tmp> up2;

    up2 = std::move(up1);  // 移动 up1 到 up2
    up2->print__a();       // 输出 5

    return 0;
}

shared_ptr对象之间的转移和拷贝的区别

  • 转移 shared_ptr 所有权的操作更高效,因为它只是修改指针的所有权,并不需要增减引用计数。
  • 拷贝 shared_ptr 的操作需要增加引用计数,因此会对引用计数造成额外的开销。
  • 在需要转移资源所有权、不再需要原始 shared_ptr 指向资源的情况下,使用转移更为适合。
  • 在需要资源共享、保持原始 shared_ptr 的有效性的情况下,使用拷贝。
  • 注意:C++11 中的 shared_ptr 默认支持 move 操作,即可以通过 std::move 来转移所有权。如果您使用的是早期版本的 C++(如 C++98/03),可能需要使用智能指针类库(如 Boost)来实现 shared_ptr 的转移操作。
#include <iostream>
#include <memory>

int main() {
    // 创建一个 shared_ptr 对象
    std::shared_ptr<int> sourcePtr = std::make_shared<int>(42);

    // 转移 shared_ptr 的所有权
    std::shared_ptr<int> destPtr = std::move(sourcePtr);

    // sourcePtr 现在为空
    std::cout << "sourcePtr: " << sourcePtr << std::endl;

    // destPtr 持有指向相同内存的共享资源
    std::cout << "destPtr: " << destPtr << std::endl;

    return 0;
}

# sourcePtr: 0
# destPtr: 0x56430331dec0

智能指针在多线程情况下使用的注意事项

在多线程情况下使用智能指针时,有几个注意事项需要考虑,以确保线程安全性和避免悬垂指针的问题。

互斥访问:在多个线程中同时使用相同的智能指针时,需要确保对指针进行互斥访问,以保证线程安全。可以使用互斥锁(mutex)或其他同步机制,例如std::lock_guard或std::unique_lock。

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

std::shared_ptr<int> sharedPtr;
std::mutex mutex;

void ThreadFunction(int a)
{
    std::lock_guard<std::mutex> lock(mutex);
    std::this_thread::sleep_for(std::chrono::seconds(a));
    // 使用 sharedPtr 进行读写操作
    *sharedPtr = a;
    std::cout<< *sharedPtr<<std::endl;
    
}

int main()
{
    sharedPtr = std::make_shared<int>(42);
    // 启动多个线程访问智能指针
    std::thread t1(ThreadFunction,2);
    std::thread t2(ThreadFunction,1);
    std::thread t3(ThreadFunction,3);
    t1.join();t3.join();t2.join();
}

std::atomic<std::shared_ptr> atomicCounter; : error: static assertion failed: std::atomic requires a trivially copy type

  • atomic仅支持科平凡赋值对象,即不包含virtual函数

多线程中使用unique_ptr

是的,可以在多线程环境中使用std::unique_ptr。std::unique_ptr是一种独占所有权的智能指针,可以确保在多线程环境下正确管理内存资源。

以下是一个示例,展示了多线程环境下使用std::unique_ptr的情况:

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

void ThreadFunction(std::unique_ptr<int> uniquePtr)
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Thread: " << *uniquePtr << std::endl;
}

int main()
{
    std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);

    std::thread t1(ThreadFunction, std::move(uniquePtr));

    t1.join();

    return 0;
}

在上述示例中,我们创建了一个std::unique_ptr对象,并将其作为参数传递给另一个线程的函数ThreadFunction。通过std::move函数进行所有权的转移,确保uniquePtr在主线程中不再持有资源。

在新线程中,我们打印出通过uniquePtr访问的整数值。在这个例子中,由于std::unique_ptr具有独占所有权,所以在传递所有权后,uniquePtr被置为null,并且在新线程中访问uniquePtr会导致异常。因此,在多线程环境中使用std::unique_ptr时,需要确保正确地进行所有权的转移,避免使用已转移所有权的uniquePtr。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值