前言
这几天在项目中发现base代码中有个关于指针shared_ptr引用使用问题,现在把问题的原因和写的demo给大家分享一下,供大家参考。
问题内容
代码中有个全局数据存储上下文信息,供UI某查询界面对查询条件进翻页分段查询。
界面示意图
后台系统收到UI的新查询时,此时请求中标志是“新请求/旧请求”标记为新请求,后台则创建并存储一个对应的“上下文信息”,用于标记当前查询条件的操作,随后到数据库查询对应条件的全量数据并存在后台系统内存中,后台系统向UI返回一定个数的数据;如果是当前查询的翻页操作,UI也会发起查询请求,此时请求中的标志为“旧请求”,后台系统需要找到对应的上下文信息并在内存中进行数据读取。
后台系统在“上下文信息”中存储了某个智能指针数据ptr,在后台的具体查询函数中,新定义了这个类型的智能指针引用类型auto & temp存储ptr数据,但是最后在使用完后,显示执行了智能指针的释放(temp=nullptr;),导致原智能指针ptr数据被释放.....
问题产生原因
把“智能指针shared_ptr”赋值给新定义新指针变量引用类型后,引用计数并没有增加, 引用变量设置为nullptr后,原智能指针内部数据也已经无效。
智能指针shared_ptr介绍
shared_ptr允许多个该智能指针共享“拥有”同一堆分配对象的内存,这通过引用计数(reference counting)实现,会记录有多少个shared_ptr共同指向一个对象,一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。支持复制和赋值操作。
- shared_ptr 类对象默认初始化为一个空指针。智能指针是模板,创建智能指针必须提供类型信息。
如:
shared_ptr<string> p1; // shared_ptr,可以指向 string
shared_ptr<list<int>> p2; // shared_ptr,可以指向 int 的 list
- 智能指针的使用和普通指针类似,解引用一个智能指针返回它指向的对象,如过在条件判断中使用它,则是检测其是否为空。
- 当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。
- 每个shared_ptr都有一个关联的计数器,通常称其为引用计数:
对shared_ptr类进行拷贝时,计数器就会增加(用一个shared_ptr初始化另一个shared_ptr、或者它作为参数传递给一个函数以及作为函数的返回值)它所关联的计数器就会增加;
当我们让shared_ptr指向另一个对象或shared_ptr对象销毁时,原对象的计数器就会递减,一旦一个shared_ptr的计数器为0,就会自动释放该对象的内存。
c++引用
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名;
- 引用引入了对象的一个同义词,定义引用的表示方法与定义指针相似,只是用&代替了*。
- 对引用求地址,就是对目标变量求地址。
智能指针shared_ptr取引用的计数效果
- 把“智能指针shared_ptr”赋值给新定义新指针变量引用类型后,引用计数并没有增加, 引用变量设置为nullptr后,原智能指针内部数据也已经无效
- 把“智能指针shared_ptr”赋值给新定义新指针变量后,引用计数增加,新指针设置为nullptr后,原智能指针内部数据不影响
(可以通过对下列代码15行处 #if 后数值改成0或1进行验证)
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr(new int(5));
std::cout << "count: " << ptr.use_count() << std::endl; // 打印引用计数, 1
// 获取原始指针并进行赋值操作
int *p = ptr.get();
*p = 10;
// 输出结果,发现值已经被修改
std::cout << *ptr << std::endl; // 输出 10
#if 1
//把“智能指针shared_ptr”赋值给新定义新指针变量引用类型后,引用计数并没有增加, 引用变量设置为nullptr后,原智能指针内部数据也已经无效
auto &t = ptr;
std::cout << "count: " << ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << t.get() << std::endl;//0x1331b20
std::cout << ptr.get() << std::endl;//0x1331b20
t = nullptr;// 显式销毁所指对象
std::cout << "count: " << ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << p << std::endl;//0x1331b20
std::cout << ptr << std::endl;//0
//std::cout << *ptr << std::endl;//异常
std::cout << ptr.get() << std::endl;//0
#else
//把“智能指针shared_ptr”赋值给新定义新指针变量后,引用计数增加,新指针设置为nullptr后,原智能指针内部数据不影响
auto t = ptr;
std::cout << "count: " << ptr.use_count() << std::endl; // 打印引用计数, 2
std::cout << t.get() << std::endl;//0x2220b20
std::cout << ptr.get() << std::endl;//0x2220b20
t = nullptr;// 显式销毁所指对象
std::cout << "count: " << ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << p << std::endl;//0x2220b20
std::cout << ptr << std::endl;//0x2220b20
std::cout << *ptr << std::endl;//输出 10
#endif
return 0;
}
我们再看看通过函数入参传入智能指针的效果:
(可以通过对下列代码11行处 #if 后数值改成0或1进行验证)
#include <iostream>
#include <memory>
struct s_test_
{
std::shared_ptr<int> ptr;
};
void fun(s_test_ & s_temp)
{
#if 1
//智能指针shared_ptr取引用后,引用计数并没有增加, 引用变量设置为nulls_temp.ptr后,原智能指针内部数据也已经无效
auto &t = s_temp.ptr;
int *p = t.get();
*p = 20;
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << t.get() << std::endl;//0x1331b20
std::cout << s_temp.ptr.get() << std::endl;//0x1331b20
t = nullptr;// 显式销毁所指对象
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << p << std::endl;//0x1331b20
std::cout << s_temp.ptr << std::endl;//0
//std::cout << *(s_temp.ptr) << std::endl;//异常
std::cout << s_temp.ptr.get() << std::endl;//0
#else
//定义新指针指向“智能指针shared_ptr”后,引用计数增加,新指针设置为nulls_temp.ptr后,原智能指针内部数据不影响
auto t = s_temp.ptr;
int *p = t.get();
*p = 20;
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 打印引用计数, 2
std::cout << t.get() << std::endl;//0x2220b20
std::cout << s_temp.ptr.get() << std::endl;//0x2220b20
t = nullptr;// 显式销毁所指对象
std::cout << "s_temp.ptr count: " << s_temp.ptr.use_count() << std::endl; // 打印引用计数, 1
std::cout << p << std::endl;//0x2220b20
std::cout << s_temp.ptr << std::endl;//0x2220b20
std::cout << *(s_temp.ptr) << std::endl;//输出 20
#endif
}
int main()
{
s_test_ s1;
s1.ptr = std::make_shared<int>(5);
std::cout << "s1.ptr count: " << s1.ptr.use_count() << std::endl; // 打印引用计数, 1
// 获取原始指针并进行赋值操作
int *p = s1.ptr.get();
*p = 10;
// 输出结果,发现值已经被修改
std::cout << *(s1.ptr) << std::endl; // 输出 10
fun(s1);
std::cout << *(s1.ptr) << std::endl; // 输出 20
return 0;
}
demo验证结果:
智能指针shared_ptr取引用
定义新指针指向“智能指针shared_ptr”