智能指针知识点归纳

3.3 智能指针
3.3.1 RAII 和 智能指针实现

智能指针使用RAII 技术将普通的指针封装为一个栈对象,当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。

智能指针的实现:

template <typename T>
class SharedPtr {
public:
    explicit SharedPtr(T* ptr = nullptr) : data(ptr), refCount(new int(1)) {}

    SharedPtr(const SharedPtr<T>& other) : data(other.data), refCount(other.refCount) {
        (*refCount)++;
    }

    ~SharedPtr() {
        if (--(*refCount) == 0) {
            delete data;
            delete refCount;
        }
    }

    SharedPtr<T>& operator=(const SharedPtr<T>& other) {
        if (this != &other) {
            if (--(*refCount) == 0) {
                delete data;
                delete refCount;
            }
            data = other.data;
            refCount = other.refCount;
            (*refCount)++;
        }
        return *this;
    }

    T& operator*() const {
        return *data;
    }

    T* operator->() const {
        return data;
    }

private:
    T* data;
    int* refCount;
};
3.3.2 shared_ptr

shared_ptr 除了内置的常见属性,常见的有两种用法:一种是使用std::shared_ptr<void>用于线程池、任务队列封装任务;另一种用于类成员智能指针;

(1)shared_ptr

​ shared_ptr 共享指针主要有个用法:reset,get, use_count;创建使用std::make_shared

std::vector<double> vec = {2, 3, 1, 4, 6, 8, 9, 7, 5, 0};
std::shared_ptr<std::vector<double>>sharePtr = std::make_shared<std::vector<double>>(vec);
std::cout << "use_count is: " << sharePtr.use_count() << std::endl;
std::vector<double> *ptr = sharePtr.get();
for(int i = 0; i<ptr->size(); i++){
  std::cout << (*ptr)[i] << ", ";
}
std::cout << std::endl;
sharePtr.reset(); // sharePtr 现在为空指针,原来的对象被释放

(2)std::shared_ptr<void>

泛型数据结构或者函数都可以被std::shared_ptr接管,并且在离开其作用域,会自动调用其析构函数。常见的使用方式:

#include <iostream>
#include <memory>
#include <functional>

// 定义任务接口
using Task = std::function<void()>;

// 任务执行函数
void executeTask(const std::shared_ptr<Task>& taskPtr) {
    if (taskPtr) {
        (*taskPtr)();
    } else {
        std::cerr << "Invalid task!\n";
    }
}

int main() {
    // 创建一个任务并封装在std::shared_ptr中
    auto taskPtr = std::make_shared<Task>([]() {
        std::cout << "Task executed!\n";
    }); 
    // 执行任务
    executeTask(taskPtr);
    return 0;
}

(3)类成员智能指针

请注意类成员智能指针是在类外进行初始化,在类内部仅声明了,在构造函数也没有任何显示;

示例代码:

#include <iostream>
#include <memory>

class Resource {
public:
    Resource(int id) : mId(id) {
        std::cout << "Resource " << mId << " created!" << std::endl;
    }

    ~Resource() {
        std::cout << "Resource " << mId << " destroyed!" << std::endl;
    }

    void doSomething() {
        std::cout << "Resource " << mId << " doing something..." << std::endl;
    }

private:
    int mId;
};

class Owner {
public:
    std::shared_ptr<Resource> mResource;

    Owner() {
        std::cout << "Owner created!" << std::endl;
    }

    ~Owner() {
        std::cout << "Owner destroyed!" << std::endl;
    }

    void useResource() {
        if (mResource) {
            mResource->doSomething();
        } else {
            std::cout << "No resource available!" << std::endl;
        }
    }
};

int main() {
    // 创建 Resource 对象并初始化 shared_ptr
    std::shared_ptr<Resource> resource = std::make_shared<Resource>(1);
    // 创建 Owner 对象并将 resource 分配给 mResource
    Owner owner;
    owner.mResource = resource;
    // 使用 Owner 对象拥有的 Resource 对象
    owner.useResource();
    // Owner 对象在 main() 函数结束时被销毁,同时 Resource 对象也会被正确地释放
    return 0;
}

3.3.3 weak_ptr

weak_ptr在指向一个对象的时候不会增加其引用计数,weak_ptr 除了内置函数,主要应用于解决shared_ptr 循环使用

(1)weak_ptr

与shared_ptr类似,weak_ptr用法主要是:lock(),expired();lock()函数类似shared_ptr的get函数,expired()类似于use_count()==0

(2)weak_ptr 无法直接访问资源,使用lock方法才能访问资源

(3)防止循环引用,weak_ptr 主要用于解决shared_ptr 循环使用

示例:

class CTxxx {
public:    
	CTxxx() {printf( "CTxxx cst\n" );}
	~CTxxx() {printf( "CTxxx dst\n" );};
};
    
int main() {
    std::shared_ptr<CTxxx> sp_ct(new CTxxx);
    std::weak_ptr<CTxxx> wk_ct = sp_ct;
    std::weak_ptr<CTxxx> wka1;
    {
        std::cout << "wk_ct.expired()=" << wk_ct.expired() << std::endl;
        std::shared_ptr<CTxxx> tmpP = wk_ct.lock();
        if (tmpP) {
            std::cout << "tmpP usecount=" << tmpP.use_count() << std::endl;
        } else {
            std::cout << "tmpP invalid" << std::endl;
        }
        std::shared_ptr<CTxxx> a1(new CTxxx);
        wka1 = (a1);
    }
    std::cout << "wka1.expired()=" << wka1.expired() << std::endl;
    std::cout << "wka1.lock()=" << wka1.lock() << std::endl;
 
    std::shared_ptr<CTxxx> cpySp = wka1.lock();
    if (cpySp) std::cout << "cpySp is ok" << std::endl;
    else std::cout << "cpySp is destroyed" << std::endl;
    return 1;
}

(2)weak_ptr循环引用

请注意强弱智能指针的一个重要应用规则:定义对象时,用强智能指针shared_ptr,在其它地方引用对象时,使用弱智能指针weak_ptr

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb; // 指向B对象的弱智能指针。引用对象时,用弱智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra; // 指向A对象的弱智能指针。引用对象时,用弱智能指针
};
int main()
{
    // 定义对象时,用强智能指针
	shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
	shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
	
    // A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptra->_ptrb = ptrb;
	// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptrb->_ptra = ptra;

	cout << ptra.use_count() << endl; // 打印结果:1
	cout << ptrb.use_count() << endl; // 打印结果:1

	/*
	出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
	B对象的引用计数从1减到0,达到释放A和B的条件,因此new出来的A和B对象
	被析构掉,解决了“强智能指针的交叉引用(循环引用)问题”
	*/
	return 0;
}

(3)weak_ptr弱回调

​ 弱回调的作用是实现了一种在事件处理器对象销毁后不再调用回调函数的机制。

​ 具体来说,假设有一个事件处理器对象 handler,它持有一个回调函数对象的弱指针 mCallback。当事件发生时,事件处理器会调用 triggerEvent() 函数,该函数会尝试将弱指针转换为 std::shared_ptr,如果成功则说明回调函数对象仍然存在,可以调用;如果失败则说明回调函数对象已经被销毁,无法调用。

#include <iostream>
#include <memory>
#include <functional>

// 定义一个事件处理器类,它持有一个弱指针
class EventHandler {
public:
    EventHandler() {}

    void setCallback(std::weak_ptr<std::function<void()>> callback) {
        mCallback = callback;
    }

    void triggerEvent() {
        // 检查弱指针是否已经过期
        if (auto callback = mCallback.lock()) {
            (*callback)();
        } else {
            std::cout << "Callback is expired!" << std::endl;
        }
    }

private:
    std::weak_ptr<std::function<void()>> mCallback;
};

int main() {
    // 创建一个事件处理器对象
    EventHandler handler;
    // 创建一个 shared_ptr,用于保存回调函数
    auto callback = std::make_shared<std::function<void()>>([]() {
        std::cout << "Event triggered!" << std::endl;
    });
    // 将回调函数传递给事件处理器
    handler.setCallback(callback);
    // 触发事件
    handler.triggerEvent();
    // 释放回调函数
    callback.reset();
    // 再次触发事件,此时因为回调函数已经被释放,会输出 "Callback is expired!"
    handler.triggerEvent();
    return 0;
}

3.3.4 unique_ptr

unique_ptr的特点是: (1)管理的资源只能有一个,不能进行拷贝,只能进行移动。(2)轻量:没有引用计数;unique_ptr 常用于vector容器组合使用,用于互斥锁之中;

**(1)unique_ptr **

​ unique_ptr 和share_ptr内置函数类似:reset,get,move ,创建函数std::make_unique

#include <iostream>
#include <memory>

int main() {
  //创建
    std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
		std::unique_ptr<int> ptr2(new int(42));
  //转移所有权
  std::unique_ptr<int> ptr3 = std::move(ptr1); // ptr1 失去了对资源的所有权
  //获取原始指针
  int* rawPtr = ptr.get();
  ptr.reset(); // 释放所有权并删除对象
}

**(2)std::vector<std::unique_ptr> **

通常情况下,需要把类放入一个容器中,常采用std::unique_ptrstd::vector 结合使用;

示例代码:

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

class MyClass {
public:
    MyClass(int data) : mData(data) {
        std::cout << "MyClass Constructor, Data: " << mData << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass Destructor, Data: " << mData << std::endl;
    }
    void doSomething() {
        std::cout << "MyClass doing something with data: " << mData << std::endl;
    }
private:
    int mData;
};

int main() {
    std::vector<std::unique_ptr<MyClass>> vec;

    // 向 vector 中添加元素
    vec.push_back(std::make_unique<MyClass>(1));
    vec.push_back(std::make_unique<MyClass>(2));
    vec.push_back(std::make_unique<MyClass>(3));

    // 访问 vector 中的元素
    for (const auto& ptr : vec) {
        ptr->doSomething();
    }

    // vector 结束生命周期时,所有元素的内存会自动释放

    return 0;
}

示例2:

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

class Resource {
public:
    Resource(int data) : mData(data) {}

    void doSomething() {
        std::cout << "Resource " << mData << " is being used." << std::endl;
    }

private:
    int mData;
};

class ResourceManager {
public:
    void addResource(int data) {
        std::lock_guard<std::mutex> lock(mMutex);
        mResources.push_back(std::make_unique<Resource>(data));
    }

    void useResources() {
        std::lock_guard<std::mutex> lock(mMutex);
        for (const auto& resource : mResources) {
            resource->doSomething();
        }
    }

private:
    std::mutex mMutex;
    std::vector<std::unique_ptr<Resource>> mResources;
};

int main() {
    ResourceManager manager;

    // 创建两个线程分别添加资源和使用资源
    std::thread addThread([&]() {
        for (int i = 0; i < 5; ++i) {
            manager.addResource(i);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    });

    std::thread useThread([&]() {
        for (int i = 0; i < 5; ++i) {
            manager.useResources();
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
    });

    addThread.join();
    useThread.join();

    return 0;
}

3.3.5 智能指针转换

static_pointer_cast

作用:改函数主要是将继承类的父类指针转换成子类指针;

示例代码:

#include <iostream>
#include <memory>

struct BaseClass {};

struct DerivedClass : BaseClass {
   void f() const {
      std::cout << "Sample word!\n";
   }
};
 
int main() {
   std::shared_ptr<BaseClass> ptr_to_base(std::make_shared<DerivedClass>());

   std::static_pointer_cast<DerivedClass>(ptr_to_base)->f();

   static_cast<DerivedClass*>(ptr_to_base.get())->f();

}
  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值