智能指针
- 前置知识
》》普通指针
智能指针介绍
智能指针可以帮助C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!
智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。
动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源
C++11后auto_ptr 已经被“抛弃”,改为unique_ptr替代!C++11后不建议使用auto_ptr,因此本位不从unique_ptr开始介绍
- 头文件
#include<memory>
- 创建
同模板类
unique_ptr<string> p1(new string("hello"));
shared_ptr<int>p2(new int(5));
API:
- get()获取智能指针托管的指针地址
unique_ptr<string> p1(new string("hello"));
cout << p1.get() << endl;
//0x55fade17eeb0
- release() 取消智能指针对动态内存的托管
unique_ptr<int>p1(new int(5));
int*pp = p1.release();
delete pp;//因为智能指针内部源码使用new所以这里可用delete
unique_ptr
两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。
无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象。
unique_ptr<int>p1(new int(5));
unique_ptr<int>p2(new int(6));
// p1 = p2; //禁止
// unique_ptr<int> p3(p1);//禁止
unique_ptr<int> p3(move(p1)); //move可以
p1 = move(p2);
- STL使用
vector<unique_ptr<int>> v;
unique_ptr<int> ptr1 (new int (5));
v.push_back(move(ptr1));
- 数组使用
nique_ptr<int[]> array(new int[5]);
shared_ptr
shared_ptr记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数加1,当智能指针析构时,引用计数减1,如果计数为零,代表已经没有指针指向这块内存,就释放它
shared_ptr<int>p1(new int(5));
shared_ptr<int>p2(new int(6));
shared_ptr<int>p3(p1);
//使用make_shared 初始化对象,分配内存效率更高
//make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr
shared_ptr<int> p4 = make_shared<int>(7);
cout << *p3; //5
p3 = p2;
cout << *p3; //6
- use_count()可以获得当前托管指针的引用计数
cout << p3.use_count(); //2
- 循环引用问题
struct Father{
shared_ptr<Son> son_;
};
struct Son{
shared_ptr<Father> father_;
};
int main()
{
auto father = make_shared<Father>();
auto son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}
互相持有导致循环引用问题,析构函数不能正常使用
weak_ptr
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。
shared_ptr<int>p1(new int(5));
weak_ptr<int>p2(p1);
cout << p1.use_count() << endl; //1
cout << p2.use_count() << endl; //1
cout << *p2 << endl; //错误
- 使用 lock 获得一个可用的 shared_ptr 对象
weak_ptr<int>p2(p1);
shared_ptr<int>p3 = p2.lock();
- weak_ptr解决循环引用问题
struct Father{
shared_ptr<Son> son_;
};
struct Son{
weak_ptr<Father> father_;
};
int main()
{
auto father = make_shared<Father>();
auto son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}
我们在会产生循环引用的位置,把shared_ptr换成weak_ptr。它不参与资源的管理,他是专门用来解决引用计数的,我们可以使用一个shared_ptr 来初始化一个weak_ptr,但是weak_ptr 不增加引用计数,不参与管理,但是也像指针一样访问修改资源。
类型转换
std::dynamic_pointer_cast
是C++标准库中的一个函数,用于执行动态类型转换操作,特别适用于智能指针(如std::shared_ptr
或std::unique_ptr
)。它类似于dynamic_cast
,但专门用于智能指针类型,可以在运行时检查并转换智能指针所指向的对象类型。
语法
template< class T, class U >
std::shared_ptr<T> dynamic_pointer_cast( const std::shared_ptr<U>& r );
或对于std::unique_ptr
:
template< class T, class U >
std::unique_ptr<T> dynamic_pointer_cast( std::unique_ptr<U>&& r );
功能
-
类型检查与转换:
dynamic_pointer_cast
会检查std::shared_ptr<U>
或std::unique_ptr<U>
所指向的对象是否能安全地转换为类型T
。如果可以(即对象实际上是类型T的实例,或者T是U的公有派生类),转换成功并返回一个指向转换后类型的新智能指针。如果对象不是所请求的类型,则返回一个空指针(对于std::shared_ptr
是nullptr
,对于std::unique_ptr
则通过重载的函数签名实现逻辑)。 -
安全性:与直接使用
dynamic_cast
类似,dynamic_pointer_cast
也是在运行时进行类型检查的,这使得它比静态类型转换(如static_pointer_cast
)更安全,因为它可以避免非法的类型转换导致的错误。
使用场景
-
多态性转换:当你有一个基类的智能指针,但需要将其转换为派生类的智能指针以便访问派生类特有的方法或属性时,可以使用
dynamic_pointer_cast
。 -
安全的向下转型:在不确定智能指针实际指向的类型是否为期望的派生类型时,使用
dynamic_pointer_cast
进行检查和转换是一种安全的做法。
示例
class Base {};
class Derived : public Base {};
int main() {
std::shared_ptr<Base> basePtr = std::make_shared<Derived>();
// 尝试转换为Derived类型
std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
if (derivedPtr) {
// 转换成功,可以安全地使用Derived的特性
} else {
// 转换失败,basePtr实际上并不指向Derived类型的对象
}
return 0;
}
在这个示例中,如果basePtr
实际指向的是一个Derived
类型的对象,那么dynamic_pointer_cast
会成功,并返回一个指向Derived
类型的智能指针;否则,返回nullptr
。