文章正文
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++ 阅读(3332) 评论(0)