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_ptrstd::shared_ptr<Resource> resource = std::make_shared<Resource>(1);// 创建 Owner 对象并将 resource 分配给 mResourceOwner 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的引用计数为1shared_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; // 打印结果:1cout << 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_ptr
与 std::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();}