文章正文
C++智能指针的内存角度的分析
写这篇文章是由于有个同事问了一个问题,虽然智能指针在C++11中就已经支持了,但是一直都没怎么用所以原理有些不太清楚,这里正好学习记录一下
理解shard_ptr,我们先看一个例子(这里都使用C++17编译)
编译命令:g++ -std=c++1z shard_ptr.cpp
#include<iostream> #include<pthread.h> #include<semaphore.h> #include<unistd.h> using namespace std; class Test{ public: int x; int *y; int getX(){return x;}; int getY(){return *y;}; }; shared_ptr<Test> *shared_data;//use for send data between threads. static sem_t *g_semaphore; void function1(){ shared_ptr<Test> t; Test t_ins; t_ins.x = 40; int y = 100; printf("y addr:%p\n",&y); t_ins.y = &y; printf("t_ins addr:%p\n",&(t_ins.x)); t = make_shared<Test>(t_ins); int *s = new int(3); printf("s addr:%p spointer addr:%p\n",&s,s); shared_data = new shared_ptr<Test>(t); } int function2(){ int x[20] = {0}; Test t; t.x = 20; return x[0] + t.getX(); } void* thread_fun2(void*){ printf("thread 2 func1\n"); function1(); printf("thread 2 func2\n"); function2();//modify stack data printf("thread_fun2 x:%d addr:%p\n",(*shared_data)->getX(),&((*shared_data)->x)); sem_post(g_semaphore); return NULL; } void* thread_fun1(void*){ sem_wait(g_semaphore); printf("thread 1\n"); shared_ptr<Test> t; t= std::move(*shared_data); //printf("thread_fun1 x:%d y:%d cout:%ld\n",t->getX(),t->getY(),t.use_count());// segmentation fault printf("thread_fun1 x:%d y:%p cout:%ld\n",t->getX(),t->y,t.use_count()); return NULL; } int main(){ static pthread_t tid1,tid2; g_semaphore=sem_open("yoursem",O_CREAT, S_IRUSR | S_IWUSR, 0); cout<<"sem_init"<<endl; pthread_create(&tid1, NULL, thread_fun1, NULL); pthread_create(&tid2, NULL, thread_fun2, NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); sem_close(g_semaphore); sem_unlink("yoursem"); return 0; }
输出:
ZacharydeMacBook-Pro:shard_ptr zachary$ ./a.out sem_init thread 2 func1 t_ins addr:0x7000042cedc0 s addr:0x7000042ced98 spointer addr:0x7f9e38500020 thread 2 func2 thread_fun2 x:40 addr:0x7f9e38500018 thread 1 thread_fun1 x:40 cout:1
流程分析:
1、线程一在栈上分配一个智能指针对象、一个Test对象、一个数值对象
2、通过make_shared将智能指针指向Test对象
3、在堆上new分配一个智能指针,并将栈上的指针传递到堆上,并将堆上的智能指针对象的地址赋值到全局变量指针上
4、通过Function2擦除栈值,然后唤醒线程一
5、线程一唤醒后读取智能指针的值
输出解析:
这里需要看的是线程二栈上的值为什么会在线程一正常打印呢?
可以看到栈上的Test对象的地址是0x7000042cedc0,在make_shared后,栈上的智能指针指向的地址已经是堆上(0x7f9e38500018)的了,所以make_shared会拷贝一份栈上的对象到堆中,然后通过实验,如果Test对象有一个指针指向了栈地址的值,我们可以看到在线程一种已经访问不到Test对象中指针所指的值了,由于C++有段访问保护,所以在程序运行的时候,会抛出段错误的异常
总结:
1、make_shared会自动分配一份到堆中,采用的是浅拷贝。
2、move会将原始的ptr释放,将其转移到被赋值的对象上
2、智能指针的对象的存放区域是有程序员自己控制的
BY THE WAY:
1、调试中有个坑,MAC上不支持sem_init创建的信号量,也没有判断返回值,导致不能有效互斥,发生Segmentation fault: 11
2、cout是异步实现的,打印出来不好看如下图,所以建议使用printf
ZacharydeMacBook-Pro:shard_ptr zachary$ ./a.out sem_init thread 2 func1 thread 2 func2 thread_fun2 t:thread 140 thread_fun1 t:40
May 12, 2019, 1:11 p.m. 作者:zachary 分类:C++ 阅读(2253) 评论(0)