1.为什么引入智能指针?
在实际开发中,经常会遇到程序运行崩溃、占用内存越来越多等问题。这往往是由于内存资源管理不当造成的。
为了解决这些问题,人们认为C++应该提供更加友好的内存管理机制,从而将精力专注于开发项目的功能上。
C++98/03标准中,支持使用auto_ptr智能指针来实现堆内存的自动回收。但C++11标准中则废弃了auto_ptr,新增了shared_ptr、unique_ptr、weak_ptr三个智能指针来实现堆内存的自动回收。(本文主要介绍shared_ptr智能指针)
2.什么是智能指针
智能指针顾名思义就是“智能”的指针,具体来看,智能指针与普通指针用法类似,区别就是,智能指针可以在适当的时机自动释放分配的内存,从而避免“忘记释放内存而导致内存泄漏”问题的出现。
C++ 智能指针底层是采用引用计数的方式实现的。智能指针在申请堆内存空间的同时,会为其配备一个整形值(初始值为 1),每当有新对象使用此堆内存时,该整形值 +1;反之,每当使用此堆内存的对象被释放时,该整形值减 1。当堆空间对应的整形值为 0 时,即表明不再有对象使用它,该堆空间就会被释放掉。
3.shared_ptr智能指针
shared_ptr是以类模板的方式实现的。定义于<memory>头文件,并位于std命名空间。因此在使用时应先包含头文件与命名空间。
特点:shared_ptr 采用引用计数的机制,允许多个 shared_ptr 共享同一个对象的所有权。每当一个新的 shared_ptr 指向该对象时,引用计数加 1;当一个 shared_ptr 被销毁或不再指向该对象时,引用计数减 1。当引用计数变为 0 时,对象被自动销毁。
4.shared_ptr的用法
(1)创建shared_ptr智能指针
shared_ptr类模板中,提供了多种实用的构造函数,可以直接使用。例如:
std::shared_ptr<int> p1; //不传入任何实参std::shared_ptr<int>
p2(nullptr); //传入空指针 nullptr
创建了一个空智能指针。
std::shared_ptr<int> p3(new int(10));
创建了一个智能指针,指向一块存有10的堆内存空间。
std::shared_ptr<int> p3 = std::make_shared<int>(10);
通过模版函数初始化智能指针,与第二种方法创建的完全相同。
除此之外,还提供了拷贝构造函数与移动构造函数。
//拷贝构造函数
std::shared_ptr<int> p4(p3);
//或 std::shared_ptr<int> p4 = p3;
//调用移动构造函数
std::shared_ptr<int> p5(std::move(p4));
//或者 std::shared_ptr<int> p5 = std::move(p4);
注意,一个普通指针不能同时为多个shared_ptr对象赋值,否者程序异常。
在初始化shared_ptr智能指针时,还可以自定义所指堆内存的释放规则,当堆内存引用计数为0时,优先调用自定义的释放规则。例如:
//指定 default_delete 作为释放规则
std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());
//自定义释放规则
void deleteInt(int*p) {
delete []p;
}
//初始化智能指针,并自定义释放规则
std::shared_ptr<int> p7(new int[10], deleteInt);
如上所示,自定义了释放规则。
使用lambda表达式,同样可以达到上面的效果。不了解lambda表达式的可以阅读C++11新特性之lambda表达式-优快云博客
std::shared_ptr<int> p7(new int[10], [](int* p) {delete[]p; });
上面简单介绍了几种初始化方法,除此之外,还有一些其他方法,感兴趣的读者可以到官网更加深入的了解。
(2)成员方法
为了方便用户使用shared_ptr智能指针,shared_ptr还提供了一些实用的成员方法,如下所示。
operator=()——重载赋值号,使同类型的shared_ptr可以相互赋值。
operator*()——重载*号,获取当前shared_ptr对象指向的数据。
operator->()——重载->号,当shared_ptr指向的数据类型为自定义类型时,可通过->获取其内部成员。
operator bool()——判断当前shared_ptr对象是否为空智能指针,
swap()——交换两个相同类型shared_ptr的内容。
reset()——主要用于改变shared_ptr的指向,停止对当前对象的管理,并将其指向一个新对象。
get()——获取shared_ptr对象内部包含的普通指针。
use_count()——返回当前shared_ptr对象指向相同的所有shared_ptr对象的数量。
unique()——判断是否有别的shared_ptr对象指向了当前shared_ptr对象所指的堆内存。
除此之外,C++11 标准还支持同一类型的 shared_ptr 对象,或者 shared_ptr 和 nullptr 之间,进行 ==,!=,<,<=,>,>= 运算。
5.总结
智能指针的优点:
(1)自动内存管理。在合适的时机自动释放内存,减少手动管理的复杂性与错误可能性。
(2)避免内存泄漏。自动处理对象的声明周期,避免了因遗忘而导致的内存泄漏问题。
(3)提高代码安全性。减少了悬空指针的出现,提高代码的健壮性。
注意事项:
(1)性能开销。shared_ptr的引用机制会带来额外的性能开销,如内存分配和引用计数的更新操作等。
(2)正确使用。根据具体场景选择合适的智能指针类型,避免不恰当的使用导致意外的结果。
如有错误,敬请指正!