指针本质上是一个内存地址索引,代表了一块内存区域,能够直接读写内存;因为指针完全映射了计算机硬件,所以操作效率高,是C/C++高效的根源。但是指针也会产生访问无效数据、指针越界或者内存分配不及时等导致的运行错误,内存泄漏,资源丢失等一系列问题。
RAII (Resource Acquisition is Initialization )资源获取即初始化,将裸指针包装起来,在构造函数里初始化,在析构函数里释放,这样当对象失效销毁时,C++会自动调用析构函数,完成内存释放,资源回收等清理工作。--->智能指针 RAII的典型应用
unique_ptr
在声明的时候必须使用模板参数指定类型
unique_ptr<int> ptr1(new int(20));
unique_ptr<string> ptr2(new string("hello"));
assert(*ptr1 = 10);//可以用*取内容
assert(*ptr2 == "xixi");
assert(ptr2->size() == 5);
unique_ptr不是指针,而是一个对象,随意不能对它调用delete,它会自动管理初始化时的指针,在离开作用域时释放内存,也没有定义加减运算,随意不能随意移动指针地址ptr++(error),这样就完全 避免了指针越界等危险操作,可以让代码更安全。
使用智能指针必须先初始化,不能不初始化声明后直接使用;未初始化的unique_ptr
表示空指针,操作空指针容易引起core dump
unique_ptr<int> ptr3;//未初始化的智能指针
*ptr3 = 1;//error 操作了空指针
在C++14中,可以调用工厂函数make_unipue()
,强制创建智能指针的时候必须初始化,同时还可以利用自动类型推导
auto ptr3 = make_unique<int>(1);//工厂函数创建只能指针
auto ptr4 = make_unique<string>("xixi");
C++14以下,也可以自己实现一个简化版的make_unique()
template<class T, class...Args> //可便参数模板
std::unique<T> my_make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));//unique_ptr<int> ptr1(new int(20));
}
unique_ptr表示指针的所有权是“唯一的”,不允许共享,任何时候只能被一个所持有。因此unique_ptr
应用了C++的转移move
语义,同时禁止了拷贝赋值,所以在向另一个unique_ptr
赋值的时候,用std::move()
函数显示地声明所有权转移,转移成功后,原来的unique_ptr变成了空指针。
shared_ptr
shared_ptr<int> ptr1(new int(10));
shared_ptr(string> ptr2(new string("hello"));
//也可以用工厂函数创建只能指针
auto ptr3 = make_shared<int>(10);
make_shared
可以被共享,即支持拷贝赋值,可以直接拷贝赋值auto ptr4 = ptr2;
其内部使用了引用计数
当引用计数减少到1就会delete释放内存。但shared_ptr的引用计数的存储和管理都是成本。
shared_ptr引用计数导致的“循环引用”问题会导致引用计数无法减到0,无法调用析构函数执行delete,最终导致内存泄漏。从而引入:
weak_ptr
弱引用专为打破循环引用而设计,之观察指针,不会增加引用计数,但在需要的时候,可以调用成员函数lock()
,获取shared_ptr
强引用。。。当循环引用的时候weak_ptr的引用计数不会增加,因而不会导致内存泄漏。