智能指针
new和delete
1:new初始化
- new未初始化值
c++
int *p = new int;//p值未定义
string *str = new string;//为空串,调用string默认构造函数
- new 初始化值
c++
int *p = new int(100);
string *str = new string(6,'a');//aaaaaa
- vector类型指针
c++
vector<int> *p = new vector<int>{1, 3, 5, 7, 9};
vector<int> mm{9, 8, 7, 6};
vector<int> *nn = &mm;
for (int i = 0; i < p->size();i++){
cout << p->at(i) << ",";
}
for (int i = 0; i < nn->size();i++){
cout << nn->at(i) << ",";
}
注意:
c++
vector<int> *p = new vector<int>{1, 3, 5, 7, 9};
//p->size()与(*p).size()等价
//p->at(i)等价于(*p).at(i)等价于(*p)[i]
for (int i = 0; i < p->size();i++){
cout << (*p).at(i)<< ",";
}
2:值初始化
c++
string *mystr = new string();//空字符串
int *p = new int();//赋值0
如果是类的值初始化,调用类的构造函数
c++
class A{
public:
A(){
cout << "call()" << endl;
}
};
int main(){
A *a = new A();
return 0;
}
运行结果:call()
不加()效果一样:
A *a = new A;
- new和auto的配合使用
c++
string *s = new string(7, 'a');
//这里s是指针,所以auto的类型是string*
auto *mystr = new auto(s);
//string **mystr = new string *(s);
cout << (*mystr)->c_str() << endl;
//cout << *(*mystr)<< endl;
- const可以和new配合使用
c++
const int *p = new const int(200);
//const int *p = new int(200);---new后可以不加const
const修饰的p指针不可以改变
*p=200;是错的
3:delete
new和delete是配合使用的,delete用于回收new分配出的内存空间,而且delete只能使用一次
去释放内放
注意:delele只能释放new分配的空间
c++int i = 100; int *p = &i; delete p;//报错:p指向的内存不是new出来的
多个指针指向同一块内存,没必要重复delete
c++int *p = new int(100); int *p2 = p;//p和p2指向同一块内存 delete p;//输出*p2会出错 delete p2;//没必要多次释放
总结
- new出来的必须delete,不然内存泄漏,会导致程序崩溃
- delete后的内存不能再次使用
- 不要对同一个内存多次delete
c++
int* p = new int(9);
delete p;
*p = 300;//释放之后使用会出错,最好置成NULL
//p=NULL;
cout << *p << endl;
-
new和delete都是运算符,不是函数。
-
new和delete
相比于malloc和free
可以干更多事---在堆上分配一个对象时,new和delete会自动调用类的构造和析构函数
也就是存在初始化能力,malloc和free没有这个能力
😄A a; sizeof(a);
---------空类的对象也占有一个字节,因为空类对象有地址,至少在内存占一个字节
4:operator new()和operator delete()
new运算符会分配内存
和调用构造函数
,这里分配内存就是调用operator new()
函数
delete运算符会调用析构函数
然后释放内存operator delete()
(顺序和new相反)
5:申请和释放数组
c++
int *p = new int[2];//中括号是数组,分配两个数据
class A{
public:
A(){}
};
int main(){
A *pa = new A[2]();
delete[] pa;//别忘【】
}
内置类型,
比如:int,double
,delete时候不需要调用析构函数int *pa=new int[4];
delete pa;
和delete[] pa;
是一样的,因为int是内置类型,不需要析构函数。
如果是自定义类对象,用new分配了一个对象数组:
A *a=new A[3] ();
这时候如果类存在自定义析构函数,必须用
delete[]
释放
注意:new的不是数组,也不要用delete[]。
智能指针总述
前提:new和delete非常容易出错
比如忘记delete
c++int *p=new int; if(con){ return;//提前返回 } delete p;
或者提前释放
比如:delete后的指针依然被继续使用
裸指针是直接new出来的指针
智能指针 :对裸指针进行了包装,最突出的优点就是自动释放
new的对象内存
四种智能指针:
c++
auto_ptr;//完全被unique_ptr取代,被摒弃
/*新标准c++11的三种智能指针都是类模板,可以给它们赋值new获得的地址*/
unique_ptr;----独占指针,同一时间内只有一个指针指向该对象。但是可以把所有权移交。
shared_ptr;----多个指针指向同一对象,最后一个指针被销毁,对象才被释放
weak_ptr;----辅助shared_ptr使用
1:shared_ptr
共享所有权,不是被一个shared_ptr拥有,而是被多个shared_ptr相互协作,有额外开销。
工作原理
:引用计数,每个shared_ptr的拷贝都指向相同的内存。
所以最后一个指向该shared_ptr指针不再指向该对象时,这个shared_ptr才会去析构所指向的内存。
c++
shared_ptr<int> p1(new int(100));
shared_ptr<int> p2 = new int(200);//×,只能显示赋初值()
//----------------------------------------------------------
shared_ptr<int> make(int val){
return shared_ptr<int>(new int(val));
}
shared_ptr<int> p3 = make(300);
智能指针和裸指针不要穿插使用
c++int *p = new int; shared_ptr<int> ptr(p);//不要这么使用 shared_ptr<int> ptr(new int);//可以
make_shared()创建对象
标准库里的函数模板,能够高效的分配和使用share_ptr,别忘了匹配参数<类型>
但是make_shared()不能指定删除器
c++
shared_ptr<int> p = make_shared<int>(100);
shared_ptr<string> s = make_shared<string>(10,'a');
shared_ptr的引用参数
当进行赋值或拷贝时,每个shared_ptr都会有一个引用参数
,用来记录有多少个指向同一内存的其他shared_ptr对象
引用参数其实就是一个计数器,用来记录当前指向同一个内存的shared_ptr的个数
1.引用计数的增加
c++
auto p1 = make_shared<int>(100);//创建一个shared_ptr,p1引用计数为1
shared_ptr<int> p2(p1);//用p1给p2拷贝,p1和p2此时引用计数为2
auto p3 = p2;//p2给p3赋值,p1,p2,p3引用计数为3
函数非引用传参,在函数体内引用参数+1
c++void myfun(shared_ptr<int> tmp) { return; } int main() { auto p1 = make_shared<int>(100);//创建一个shared_ptr,p1引用计数为1 myfun(p1); }
如果传的是引用,那么引用参数不增加
c++void myfun(shared_ptr<int>& tmp) {//引用 return; } int main() { auto p1 = make_shared<int>(100);//创建一个shared_ptr,p1引用计数为1 myfun(p1); }
- 接受make_shared返回值,引用参数加1
c++shared_ptr<int> getPtr(shared_ptr<int>& p) { return p; } int main() { auto p1 = make_shared<int>(100);//创建一个shared_ptr,p1引用计数为1 auto p2 = getPtr(p1); getPtr(p2);//临时对象没有参数接收,执行后引用参数不增加 }
2.引用计数的减少
-
局部的值传递shared_ptr离开作用域
-
shared_ptr指向了新对象
c++auto p1 = make_shared<int>(100); auto p2 = getPtr(p1); auto p3 = p2; /*指针指向新对象,引用计数-1*/ p1 = make_shared<int>(33);
- 当一个shared_ptr的引用计数从1变为0,智能指针自动释放
c++
auto p1 = make_shared<int>(20);
auto p2 = make_shared<int>(30);
p1 = p2;//p1和p2引用计数为2,原来p1的内存被释放
shared_ptr指针常用操作
- use_count()---返回指向某个对象的引用计数
c++
shared_ptr<string> ptr(new string(5, 'a'));
int cnt = ptr.use_count();//1
shared_ptr<string> p2(ptr);
cout << ptr.use_count();//2
shared_ptr<string> p3 = p2;
cout << ptr.use_count();//3
- unique()----该智能指针是否独占对象
为空也不行,必须use_count()为1
c++
shared_ptr<string> ptr(new string(5, 'a'));
cout << ptr.unique() << endl;//真
shared_ptr<string> p2(ptr);
cout << p2.unique() << endl;//假
- reset()---复位
- 不带参数,把唯一指向该对象的指针的内存释放,并把指针置空。
- 不带参数,但是多个指向该对象,把引用计数-1,并把指针置空。
c++
shared_ptr<string> ptr(new string(5, 'a'));
shared_ptr<string> p2(ptr);
p2.reset();
if (p2 == nullptr) {
cout << "p2 is NULL" << endl;
}
cout << ptr.use_count() << endl;
- 带参数,把唯一指向该对象的指针的内存释放,并把指针指向新对象。
- 带参数,但是多个指向该对象,把引用计数-1,并把指针指向新对象。
shared_ptr<int> p1(new int(100));
p1.reset(new int(200));`
c++
shared_ptr<int> p1(new int(100));
auto p2(p1);
p1.reset(new int(200));
if (p1.unique()) {
cout << "p1 is unique;" << endl;
}
if (p2.unique()) {
cout << "p2 is unique;" << endl;
}
空指针也可以用reset()初始化
shared_ptr p1;***
p1.reset(new int(30));***
- 解引用---获得指针指向的对象
用法同普通指针
c++
shared_ptr<int> p1=make_shared<int>(99);
cout << *p1 << endl;//99
shared_ptr<string> p2 = make_shared<string>(5, 'a');
cout << *p2 << endl;//aaaaa
- get()---返回保存的指针,小心使用,如果释放了所指向对象,指针无效
说白了返回裸指针,这个函数目的就是某些第三方函数传参只能使用裸指针
c++shared_ptr<int> p = make_shared<int>(99); int* ptr = p.get(); cout << *ptr << endl;//99 //千万不能delete该裸指针
- swap()----交换两个对象内容
c++
shared_ptr<string> p1 = make_shared<string>("aaabbb");
shared_ptr<string> p2(new string("cccddd"));
swap(p1, p2);
cout << *p1 <<","<< * p2 << endl;
p1.swap(p2);
cout << *p1 << "," << *p2 << endl;
=nullptr
----置空
c++
shared_ptr<string> p1 = make_shared<string>("aaabbb");
shared_ptr<string> p2(p1);
p1 = nullptr;
//p1是空指针,p2指向aaabbb且引用计数为1
if (!p1) {
cout << "空指针" << endl;
}
shared_ptr指定删除器(自定义删除)
在参数中指定删除器函数名即可,自己必须delele对象,不然内存泄漏。
注意make_shared这种方式不能指定删除器
可以用lambda表达式
c++shared_ptr<int> p1(new int(100), [](int* p) { cout << "****" << endl; delete p; });//加入参数
c++
//当引用计数为0,自动调用该函数
void myDelete(int* p) {
cout << "call myDelete!" << endl;
delete p;
}
int main() {
shared_ptr<int> p1 (new int(100),myDelete);//加入参数
auto p2(p1);
p2.reset();
p1 = nullptr;
}
用shared_ptr管理动态数组,只能自己释放
c++shared_ptr<int> p1(new int[10], [](int* p) { delete[] p; });//加入参数
c++//shared_ptr<A> ptr(new A[5]);//系统释放异常。系统delete是ptr的裸指针。 shared_ptr<A> ptr(new A[5], [](A* p) { delete[] p; });
可以用default_delete来释放,<>指定数组
c++shared_ptr<A> ptr(new A[5], default_delete<A[]>());
在最开始加个[]就可以正常释放数组
c++shared_ptr<A[]> ptr(new A[5]); shared_ptr<int[]> p(new int[3]{1,2,3}); cout << p[2] << endl;//加了[]可以下标访问
函数模板来定义数组
c++
template<typename T>
shared_ptr<T> make_arr(size_t sz) {
return shared_ptr<T>(new T[sz], default_delete<T[]>());
}
int main() {
shared_ptr<int > p1= make_arr<int>(3);
shared_ptr<string > p2 = make_arr<string>(4);
}
注意:就算两个shared_ptr指定了不同的删除器,只要指向的对象类型相同,这两个shared_ptr也是一个类型
类型相同就是可以用同一个容器装
c++shared_ptr<int> p1(new int(100), f1); shared_ptr<int> p2(new int(200), f2); vector<shared_ptr<int>> vec{ p1,p2 };
c++
auto f1 = [](int *p) {
cout << "f1()" << endl;
delete p;
};
auto f2 = [](int* p) {
cout << "f2()" << endl;
delete p;
};
shared_ptr<int> p1(new int(100), f1);
shared_ptr<int> p2(new int(200), f2);
p2 = p1;//p2指向其他,调用f2释放自己对象,p1的引用计数+1
//最后执行结束前,p2和p1用f1释放
2:weak_ptr
weak_ptr用来辅助shared_ptr<进行工作
- 强指针就是:shared_ptr
- 弱指针就是:weak_ptr
weak_ptr也是一个类模板 构造的智能指针,weak_ptr指向一个由shared_ptr管理的对象
❗️❗️注意❗️❗️
- weak_ptr这种智能指针不控制所指向的对象的生命周期
如果shared_ptr和weak_ptr指向同一对象,或者说将weak_ptr绑定到shared_ptr上,并不会改变shared_ptr的引用计数。即:weak_ptr的构造和析构不能增加或减少所指向对象的引用计数。
弱引用weak_ptr是监视shared_ptr生命周期用的,weak_ptr不是一种独立的智能指针,不能用来操作所指向的资源。
注意:只有强引用才能影响对象的生命周期
c++/*一般用一个shared_ptr来初始化weak_ptr*/ shared_ptr<int> ps = make_shared<int>(100); weak_ptr<int> ps_w(ps); //弱引用赋值给弱引用 weak_ptr<int> ps_w2 = ps_w;
lock()
因为weak_ptr指向的对象的声明周期可能结束了,所以weak_ptr使用对象前,必须判断对象是否存在
lock()就是检查weak_ptr指向的对象是否存在
- 如果存在,lock()返回一个该对象的shared_ptr,此时引用计数+1
- 如果不存在,lock()返回一个空的shared_ptr
c++
int main() {
/*一般用一个shared_ptr来初始化weak_ptr*/
shared_ptr<int> ps = make_shared<int>(100);
weak_ptr<int> ps_w(ps);
//pi接受ps_w指向对象的强引用
auto pi = ps_w.lock();
if (pi) {
*pi = 99;//改变值
}
else {
cout << "no duixiang!!" << endl;
}
cout << *ps << endl;//99,改变值成功
}
c++
int main() {
/*一般用一个shared_ptr来初始化weak_ptr*/
shared_ptr<int> ps = make_shared<int>(100);
weak_ptr<int> ps_w(ps);
//pi接受ps_w指向对象的强引用
ps.reset();//减少一个强引用,此时指向的对象被释放
auto pi = ps_w.lock();
if (pi) {
*pi = 99;//改变值
}
else {
cout << "no duixiang!!" << endl;
}
}
也就是weak_ptr可以判断指向对象存在性
use_count()
获取weak_ptr的强引用计数
c++
int main() {
auto p1 = make_shared<int>(11);//p1是shared_ptr
auto p2(p1);//p2是shared_ptr
weak_ptr<int> pw(p1);
cout << pw.use_count() << endl;//2
}
expired()
是否过期的意思,弱指针的use_count为0时候返回true。也就是判断指向对象的存在性
c++
int main() {
auto p1 = make_shared<int>(11);//p1是shared_ptr
auto p2(p1);//p2是shared_ptr
weak_ptr<int> pw(p1);
p1.reset();
p2.reset();
if (pw.expired()) {
cout << "对象已经过期" << endl;
}
}
reset()
将弱引用指针涉为空,弱引用计数-1
c++
int main() {
auto p1 = make_shared<int>(11);//p1是shared_ptr
auto p2(p1);//p2是shared_ptr
weak_ptr<int> pw(p1);
p1.reset();
pw.reset();//执行后,p2指向对象没有弱引用
cout << *p2 << endl;//11
}
综合使用
c++int main() { auto p1 = make_shared<int>(100); weak_ptr<int> pw = p1; if (!pw.expired()) {//pw对象存在 auto p2 = pw.lock(); if (p2) { cout << p2.use_count() << endl; } } //p2离开了作用域,引用计数-1 cout <<"***"<< p1.use_count() << endl; }
运行结果:
2
***1
智能指针尺寸
c++
C++的一个指针占内存几个字节?
----在64位编译模式下,指针的占用内存大小是8字节
----在32位编译模式下,指针占用内存大小是4字节
智能指针大小是普通指针的两倍
c++int* p; shared_ptr<int> ps; weak_ptr<int> pw; cout << sizeof(p) << endl; cout << sizeof(ps) << endl; cout << sizeof(pw) << endl;
运行结果:
8
16
16
智能指针内部包含两个裸指针
- 一个指针指向对象
- 第二个指针指向控制块(一个很大的数据结构)
- 控制块有:强引用计数,弱引用计数,其他数据比如自定义删除器
❗️其中❗️:
shared_ptr创建控制块,weak_ptr指向控制块
这个
控制块
是第一个创建shared_ptr的对象创建make_share():一定可以创建出一个控制块
裸指针可以创建一个控制块(但是不要用裸指针初始化多个)
❗️shared_ptr使用深入
利用函数创建shared_ptr
c++//从函数中return shared_ptr shared_ptr<int> create_ptr(int val){ return make_shared<int>(val); } //利用对象来接shared_ptr,返回对象 shared_ptr<int> myPtr(int val){ shared_ptr<int> tmp = create_ptr(val*2); //系统根据tmp这个局部变量产生一个临时变量往回返 return tmp; } int main(){ shared_ptr<int> p1 = create_ptr(100); shared_ptr<int> p2 = myPtr(200); cout << *p1 << "," << *p2 << endl; }
shared_ptr使用陷阱
- 慎用裸指针
c++void profun(shared_ptr<int> ptr){ cout << ptr.use_count() << endl; return; } int main(){ int *p = new int(11); shared_ptr<int> p2(p); //profun(p);--注意裸指针和智能指针不存在隐式转换 profun(p2); *p = 300; //利用裸指针创建的智能指针后续值会变化 cout << *p << "," << *p2 << endl;//300,300 }
而且利用裸指针创建的智能指针,后续值会随着裸指针变化。所以最好后续不要用裸指针
c++void profun(shared_ptr<int> ptr){ cout << ptr.use_count() << endl; return; } int main(){ int *p1 = new int(11); //这样临时对象传入函数,引用计数不+1,所以return后内存释放 profun(shared_ptr<int>(p1)); cout << *p1 << endl; shared_ptr<int> p2(new int(100)); //但是直接创建智能指针,传入函数引用计数+1 profun(p2); }
发现p1内存异常,因为临时对象传入profun()不会增加引用计数,所以函数运行结束内存会释放
但是直接传入shared_ptr,引用计数会增加,不会影响主程序的p2
不要用裸指针初始化多个shared_ptr
c++int *p1 = new int(11); shared_ptr<int> p2(p1); shared_ptr<int> p3(p1); //1,1 cout << p2.use_count() << "," << p3.use_count() << endl;
发现p2,p3不会增加引用计数,所以释放时候会delete2次,会异常
- 慎用get()返回的指针
❗️get返回的是智能指针中的裸指针,这个指针不能delete
c++shared_ptr<int> p(new int(100)); int* pm = p.get(); //pm虽然得到了p的指针,但是内存管理还是归智能指针 //裸指针不可以释放内存 delete pm;
❗️不能将其他智能指针绑定到get的裸指针
这就是不增加引用计数,会delete多次
c++shared_ptr<int> p(new int(100)); int* pm = p.get(); shared_ptr<int> q(pm); //都是1,因为这样和绑定裸指针一样,不增加引用计数 cout << p.use_count() << "," << q.use_count();
get的裸指针不能delete而且也不用该指针赋值或初始化
- 不要把类指针this作为shared_ptr返回,改用enable_shared_from_this
c++class CT { public: shared_ptr<CT> getself() { return shared_ptr<CT>(this); } }; int main(){ shared_ptr<CT> pt(new CT); shared_ptr<CT> p2 = pt; }
这样p2,pt的引用计数都是2
c++class CT { public: shared_ptr<CT> getself() { return shared_ptr<CT>(this); } }; int main(){ shared_ptr<CT> pt(new CT); //说白了this也是类的裸指针,初始化了多个对象 //这里引用计数不增加 shared_ptr<CT> p2 = pt->getself(); cout << p2.use_count() << endl; }
它们指向同一内存但是不是同样的内存块
enable_shared_fron_this是类模板
c++//公有继承了enable_shared_from_this模板 class CT :public enable_shared_from_this<CT>{ public: shared_ptr<CT> getself() { return shared_from_this();//模板中的方法 } }; int main(){ shared_ptr<CT> pt(new CT); shared_ptr<CT> p2 = pt->getself(); cout << p2.use_count() << endl; }
这样就可以增加引用计数了
原理就是:enable_shared_from_this里面有一个弱指针,这个弱指针指向this,然后每次创建新对象就会调用lock(),保证了shared_ptr的引用计数增加
移动语义
c++shared_ptr<int> p1(new int(100));//p1地址:0x000001f36d6d0850 //这样p1为空,p2指向了内存 shared_ptr<int> p2(move(p1));//p2地址:0x000001f36d6d0850 /*move函数:让p2接管了p1的地址,然后把p1置空*/ shared_ptr<int> p3; p3 = move(p2);//同样p3接管p2,p2置空 if (!p2) { cout << "p2 id NULL" << endl; }
shared_ptr还有分配器,涉及内存分配。
c++
//使用make_shared更高效
shared_ptr<string> p1(new string("good for you"));//分配两次内存
shared_ptr<string> p2=make_shared("good for you");//释放一次内存
3:unique_ptr
unique_ptr是独占对象,所以同一时间只能由一个unique_ptr指向对象
unique_ptr被销毁的时候,它所指向的对象也会被销毁
初始化
c++
int main() {
unique_ptr<int> p;
if (p == nullptr) {
cout << "p是空指针" << endl;
}
unique_ptr<int> p2(new int(100));
//c++14才有make_unique(),这个创建不能指定删除器
unique_ptr<int> p3 = make_unique<int>(200);
cout << *p2 << "," << *p3 << endl;
}
unique_ptr不支持的操作
核心就是unique_ptr独占内存
移动语义
release()
将智能指针和对象联系切断,放弃指针的控制权。返回裸指针,然后把智能指针置空
返回的裸指针可以delete也可以给其他指针赋值
c++
unique_ptr<string> p1 = make_unique<string>("good luck");
//p2接管p1,p1置空
unique_ptr<string> p2(p1.release());
if (p1 == nullptr) {
cout << "p1被置空" << endl;
}
p2.release();
//如果没有delete那么内存泄露
正确释放
c++string* tmp=p2.release(); delete tmp;
reset()
- reset()无参数----释放智能指针指向的对象并将智能指针置空
- reset()带参数----释放智能指针指向的对象并将智能指针指向新对象
c++
unique_ptr<string> p1 = make_unique<string>("good luck");
unique_ptr<string> p2 = make_unique<string>("hello world");
p1.reset();
if (p1 == nullptr) {
cout << "p1被置空" << endl;
}
p1.reset(p2.release());//p2release后被置空,reset释放p1指向的内存,让p1指向p2的裸指针
if (p1) {
cout << *p1<< endl;
}
c++
unique_ptr<string> p1 = make_unique<string>("good luck");
p1.reset(new string("hello world"));
cout << *p1 << endl;//hello world
c++
unique_ptr<string> p1 = make_unique<string>("good luck");
//释放p1指向对象的内存
//并将p1置空
p1 = nullptr;
指向数组
一般不用,因为容器更好用
get()
返回智能指针的裸指针,但是要注意不要提前释放
c++
unique_ptr<string> up = make_unique<string>("hello,world");
string* tmp = up.get();
cout << *tmp << endl;
一般是作为库函数的函数接口参数
转换成shared_ptr
c++
auto myfun() {
return unique_ptr<string>(new string("hello, world"));
}
int main() {
//注意:unique_ptr没有控制块
//p在这里创建
shared_ptr<string> p = myfun();
unique_ptr<string> mm(new string("good,luck"));
shared_ptr<string> nn = move(mm);
}