指针一直都是c\c++头疼的东西,虽然方便,但是能不用动态内存分配就不用,因为你new 多了后在释放后程序中就会有很多碎片,而且释放已经引用会使用不规范会导致程序崩溃
C++后面其实有很多东西都是从很老版本的boost库引入进来了,比如functional
智能指针等等 智能指针就是采用了RAII思想下期再讲
因为raw指针经常会导致内存泄漏,非法内存访问,多次释放同一块内存,释放非法动态内存等问题
C++通过模板提供了更加方便的对raw指针包装的智能指针,作为对象 他使用上类似raw指针
可以避免raw指针的问题 智能指针不需要显示调用delete 他会自动释放不在使用的内存
智能指针有三种:shared_ptr,unique_ptr,weak_ptr,他们在头文件中
他们也在标准std中
1.独享指针
*即一个T 类型的指针变量可以存储动态内存地址,也可以存储一个程序块的普通变量地址
这种指针称为 raw指针(原始指针),因为除了内存地址外他没有存储别的东西
raw指针指向的动态分配内存应该及时释放,并且对new分配的内存要用delete释放
对于new[] 分配的内存要用delete[]释放,如果一个指针变量在指向新的内存时
没有释放原来的动态内存,就会造成内存泄漏
如果多次释放同一块动态内存也会引起同样的问题而导致程序崩溃
假设有2个自主指向同一块内存,如果通过一个指针释放了这块内存空间
但又通过另外一个指针访问这个块内存空间,也会引起非法内存访问的严重问题
template<typename T>
unique_ptr<T>MOVE() {
auto p = unique_ptr<T>{ new T };
return p;
}
void raw() {
auto* p = new int;
auto* q{ new int[3] };
auto i{ 3 };
p = &i;
//delete p; // 错 p指向的不是动态内存 因为p之前指向了i,而i不是动态内存,导致delete p也会导致程序崩溃
//delete q; // 错 只是释放了q指向的3个int内存块中的第一个int内存块,导致另外2个内存块无法释放,也会内存泄漏
delete[] q; // 这样就释放了所以的内存块 如果用错了delete 也会造成内存汇漏
p = new int; // 重新分配p
q = p; //将q指向p
delete p; // 删除p
p = 0; // 因为p,q指向同一个内存然后delete p释放了这块内存,但是q不知道,继续用q获取这块内存就导致非法内存访问
auto j{ *q };
}
void Unique_ptr() { // unique_ptr<>对象是对raw指针的包裹,将一个T *类型的动态分配内存块的raw指针
//作为unique_ptr<T>构造函数的参数就可以创建一个对象,在它退出作用于时销毁
//析构函数会自动释放其包含的raw指针的动态内存 他是一个独享指针不能复制拷贝给其他同对象但是支持移动语句
unique_ptr<double>p{ new double{0.} };
*p = 3.14; // 和指针一样*解引用,->,p内存
cout << *p << "\t";
double* pb = p.get(); // p是对象可以用.来访问成员
*pb = 3.1415;
cout << *p << "\t";
cout << *p.get() << "\t";
p.reset(new double); // 将一个新的 raw指针传递给他,原来的raw指针指向的内存被自动释放
cout << *p.get() << "\t"<< *pb<<endl; // pb也被释放了因为指向的是同一块内存 释放后地址就不同了
p.reset();//没丢参数就释放raw指针内存然后设为nullptr指针
cout << p.get()<<"\t";
p.reset(new double);
double* rawp = p.release(); // release()函数返回raw指针并将raw指针设置为空指针,并将p内部的raw指针设为空
*rawp = 3.0; // 然后重新指向
cout << p.get() << "\t"<<*rawp<<endl;
unique_ptr<char[]>ch{ new char[5] };
char* c = &ch[0];
ch[0] = 'a', ch[1] = 'b', ch[2] = 'c';
for (int i = 0; i != 3; i++) {
cout << ch[i] << " "<<*c++<<ends; // 当然和raw指针一样但是不能用*ch++来偏移但是可以用原始指针偏移
}// 如果直接打印ch就把整个数组打印出来了
cout << endl;
//unique_ptr<int[]>in{ new int[3] };
//unique_ptr<int[]>os{ new int[3] };
//in[0] = 0, in[1] = 1, in[2] = 2;
os = in; 错误
//os[0] = in[0]; // 元素可以复制移动,但是对象就不能了 因为他是独享指针 原始指针可以用
unique_ptr<int>n{ new int(3) };
unique_ptr<int>m{ new int };
//m = n; 但是对象就不行了 m=&n都不行
*m = *n;
cout << *m << " " << *n << endl;
//auto move = n; 会报错 但是建议用移动语句
auto move = MOVE<int>(); // 所以可以用返回的对象将其赋予
auto move1 = get_unique();
auto move2 = std::move(move); // 就是需要用右值来给unique_ptr来赋予对象
}
unique_ptr<int>get_unique() { // 移动语句
auto p = unique_ptr<int>{ new int{2} };
return p;
}
void Make_unique() { // make_unique<>()可以很方便的帮助创造一个unique_ptr<>指针并动态分配
auto p = make_unique<int>(5); // 这样只是 分配了1个int动态内存吧3赋给他
cout << *p.get() << endl;
p.reset();
auto q = make_unique<int[]>(3);
for (int i = 0; i != 3; i++) {
q[i] = i;
cout << q[i] << ends; // *q.get是0 0 0因为没偏移
}
}
int main() {
Unique_ptr();
Make_unique();
while (true);
return 0;
}
2.共享指针
shared_ptr<>和unique_ptr<>一样也是对raw指针的包装,并可以以类似的使用但他是可以通过
拷贝构造函数或赋值运算符的 任意赋值这些赋值对象将共享同一个raw指针,即同一块地址
所以它被称为 共享指针 因为多个shared_ptr 共享同一个raw指针 所以内部维护了一个引用计数器 计指针个数
当shared_ptr 指针内部的这个引用计数器变为0时析构函数才是真正释放这块内存 如果不是0析构函数仅是将计数器减少1
#include <iostream>
#include <memory>
#include <string>
using namespace std;
int main() {
auto ptr = make_shared<string>("hello");
shared_ptr<int[]>n{ new int[3] };
cout << *ptr << "\t" << ptr.use_count() << endl; // 当前内存块引用计数为1
auto p = ptr;
*p += " world";
cout << *ptr << "\t" << ptr.use_count() << endl;
cout << *p << "\t" << p.use_count() << endl;
ptr.reset(); //ptr设置为一个空指针 use_count -1 同样也可以释放在重新分配地址
//cout << *ptr << "\t" << ptr.use_count() << endl; 再次调用就会报错 因为*没有元素
cout << ptr << "\t" << ptr.use_count() << endl; // 地址是0
cout << *p << "\t" << p.use_count() << endl;
while(true);
return 0;
}
// shared_ptr<> 的引用计数是原子操作,因此是线程安全的,对于共享同一个对象的多线程,可以为每
// 一个线程定义一个shared_ptr<>,这些shared_ptr<>共享同一个对象就可以使每个线程都能访问这个对象
3.弱指针
weak_ptr<>称弱指针 是一个配合shared_ptr指针的智能指针 shaered_ptr是一种具有
所有权的指针,每个shared_ptr都拥有它指向的对象的所有权 这个权是通过引用计数实现
weak_ptr<>只能从一个shared_ptr<>创建,他是对由共享指针管理的对象的非拥有(弱)引用
即weak_ptr<>不拥有共享指针的管理对象,但是可以用弱指针查询共享指针 管理对象
所有weak_ptr<>是shared_ptr<>的观察者 观察对象是否被销毁,如果没销毁者weak_ptr可以
转换成shared_ptr<>而承担起临时所有权,如果销毁了元素的共享指针则会延迟对象生命周期
#include <iostream>
#include <memory>
using namespace std;
weak_ptr<int>gw;
void observe() {
cout << "use_count == " << gw.use_count() << ":";
if (auto spt = gw.lock())
cout << *spt << endl;
else
cout << "gw is epired\n";
}
int main() {
{
auto sp = make_shared<int>(42);
gw = sp;
observe(); //1:42
} //出了这个区域就被释放了
observe();//0:is
while (true);
return 0;
}