推荐B站视频:
2.unique_ptr_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p=2&vd_source=a934d7fc6f47698a29dac90a922ba5a3
3.unique_ptr与函数调用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y187uL?p=3&vd_source=a934d7fc6f47698a29dac90a922ba5a3一、独占指针:unique_ptr
- 任何给定的时刻,只能有一个指针管理内存
- 当指针超出作用域时,内存将自动释放
- 该类型指针不可Copy,只可以Move
二、三种创建方式
- 通过已有裸指针创建
- 通过new来创建
- 通过std::make_unique创建(推荐)
准备好头文件和源文件:
- cat.h
#ifndef CAT_H
#define CAT_H
#include <string>
#include <iostream>
class Cat{
public:Cat(std::string name);Cat() = default;~Cat();void catInfo() const {std::cout<<"cat info name : "<<m_name<<std::endl;}std::string get_name() const{return m_name;}void set_cat_name(const std::string &name) {this->m_name = name;}
private:std::string m_name{"Mimi"};
};
#endif
- cat.cpp
#include "cat.h"
Cat::Cat(std::string name) :m_name(name) {std::cout<<"Constructor of Cat : "<<m_name<<std::endl;
}Cat::~Cat() {std::cout<<"Destructor of Cat"<<std::endl;
}
1.通过已有裸指针创建
(1)在栈上
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// stack 在main函数下,会在最后一条语句之后再执行析构函数Cat c1("maomao");c1.catInfo();{ // 在这个作用域下,结束后会自动销毁Cat c1("Tom");c1.catInfo();}cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : maomao
cat info name : maomao
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\build>
(2)在堆上
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全Cat* c_p1 = new Cat("Jerry");c_p1->catInfo();{ Cat* c_p1 = new Cat("JiaFeiMao");c_p1->catInfo();}c_p1->catInfo();cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : Jerry
cat info name : Jerry
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
cat info name : Jerry
over~
PS D:\Work\C++UserLesson\cppenv\build>
- 没有执行析构函数,怎么办呢?只能手动释放
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全Cat* c_p1 = new Cat("Jerry");c_p1->catInfo();{ Cat* c_p1 = new Cat("JiaFeiMao");c_p1->catInfo();// 需要手动释放delete c_p1;c_p1 = nullptr; // 防止野指针}c_p1->catInfo();// 需要手动释放delete c_p1;c_p1 = nullptr; // 防止野指针cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\build> ."D:/Work/C++UserLesson/cppenv/build/app.exe"
Constructor of Cat : Jerry
cat info name : Jerry
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
Destructor of Cat
cat info name : Jerry
Destructor of Cat
over~
PS D:\Work\C++UserLesson\cppenv\build>
接着再来一个例子:
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{ i_p1 = new int(200);}cout<<*i_p1<<endl;// 很糟糕,说明没有自动回收该资源cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
200
over~
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
没有执行析构函数,怎么办呢?只能手动释放
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{ i_p1 = new int(200);// 需要手动释放delete i_p1; i_p1 = nullptr;}cout<<"over~"<<endl; return 0;
}
但手动释放的时候,还再多释放一次或者再访问会崩溃
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// roll pointer 原始指针 非常不安全int* i_p1 = new int(100);{ i_p1 = new int(200);// 需要手动释放delete i_p1; i_p1 = nullptr;}cout<<*i_p1<<endl;// 需要手动释放delete i_p1; i_p1 = nullptr;cout<<"over~"<<endl; return 0;
}
总结:roll pointer 原始指针 非常不安全
2.通过new来创建
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;int main(int argc,char* argv[]) {// unique_point Cat* c_p2 = new Cat("ruite");std::unique_ptr<Cat> u_c_p2(c_p2);// c_p2还能用吗?// 否则如下:无法独占c_p2->catInfo(); //ruiteu_c_p2->catInfo(); //ruitec_p2->set_cat_name("lanmao");u_c_p2->catInfo(); //lanmaocout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : ruite
cat info name : ruite
cat info name : ruite
cat info name : lanmao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
解决无法独占的问题:
- 方法一:
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// unique_point Cat* c_p2 = new Cat("ruite");std::unique_ptr<Cat> u_c_p2(c_p2);c_p2 = nullptr;u_c_p2->catInfo();cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : ruite
cat info name : ruite
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
- 方法二:
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 第二种 newstd::unique_ptr<Cat> u_c_p3{new Cat("hongmao")};u_c_p3->catInfo();u_c_p3->set_cat_name("heimao");u_c_p3->catInfo();cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : hongmao
cat info name : hongmao
cat info name : heimao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
3.通过std::make_unique创建(推荐)
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 推荐创建方式 第三种 std::move_uniquestd::unique_ptr<Cat> u_c_p4 = make_unique<Cat>();u_c_p4->catInfo();u_c_p4->set_cat_name("huangmao");u_c_p4->catInfo();cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
cat info name : Mimi
cat info name : huangmao
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
三、get()与->和解引用
- unique_ptr可以通过get()获取地址
- unique_ptr实现了->与*- 可以调用->调用成员函数- 可以调用*调用 deferencing
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc,char* argv[]) {// 推荐创建方式 第三种 std::move_uniquestd::unique_ptr<Cat> u_c_p4 = make_unique<Cat>();std::unique_ptr<int> u_i_p4 = make_unique<int>(200);u_c_p4->catInfo();u_c_p4->set_cat_name("huangmao");u_c_p4->catInfo();cout<<"============================"<<endl;cout<<*u_i_p4<<endl;cout<<"int address: "<<u_i_p4.get()<<endl;cout<<"Cat address: "<<u_c_p4.get()<<endl;cout<<"over~"<<endl; return 0;
}
执行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
cat info name : Mimi
cat info name : huangmao
============================
200
int address: 00000257966B71C0
Cat address: 00000257966C3450
over~
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
四、unique_ptr 与函数调用
第三节:unique_ptr与函数调用- unique_ptr是不可Copy,只可以Move- 在做函数参数或返回值中一定要注意所有权函数调用与unique_ptr注意事项- Passing by value- 需要用std::move来转移内存拥有权- 如果参数直接传入std::make_unique语句,自动转换为move- Passing by reference- 如果设置参数为const则不能改变指向- 比如说reset()- reset()方法为智能指针清空方法- Return by value- 指向一个local object- 可以用作链式函数
1.pass by value
(1)需要用std::move来转移内存拥有权
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {// 1.pass by valuestd::unique_ptr<Cat> c1 = make_unique<Cat>("Tom");do_with_cat_pass_value(std::move(c1));return 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
如果在do_with_cat_pass_value(std::move(c1));多加一句:c1->catInfo(); // 此时c1已经无效
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {// 1.pass by valuestd::unique_ptr<Cat> c1 = make_unique<Cat>("Tom");do_with_cat_pass_value(std::move(c1));c1->catInfo(); // 此时c1已经无效return 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : Tom
cat info name : Tom
Destructor of Cat
cat info name :
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
(2)如果参数直接传入std::make_unique语句,自动转换为move
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_value(std::unique_ptr<Cat> c) {c->catInfo();
} int main(int argc,char* argv[]) {do_with_cat_pass_value(make_unique<Cat>("JiaFeiMao")); //movereturn 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : JiaFeiMao
cat info name : JiaFeiMao
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
- 总结:如果是pass by value ,传进去以后,这个值就不存在了。它的所有权就转移了,转移到函数里边,函数结束之后就直接消失了
2.pass by reference
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();c.reset();
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); // c2->catInfo(); // 此时c2已经无效cout<<"address: "<<c2.get()<<endl;return 0;
}
运行过程:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
Destructor of Cat
address: 0000000000000000
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
注意:如果do_with_cat_pass_ref函数里不添加c.reset();那么c2->catInfo();还是可以的
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();// c.reset();
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); c2->catInfo(); cout<<"address: "<<c2.get()<<endl;return 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
cat info name : TomCat
address: 0000018F1CF14640
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
也可以加上const,使得do_with_cat_pass_ref函数内不能使用reset
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;void do_with_cat_pass_ref(const std::unique_ptr<Cat>& c) {c->set_cat_name("TomCat");c->catInfo();// c.reset();// 如果加上了const,无法使用reset()
}int main(int argc,char* argv[]) {// 2. pass by ref// (不加const)std::unique_ptr<Cat> c2 = make_unique<Cat>("heimao");do_with_cat_pass_ref(c2); c2->catInfo(); cout<<"address: "<<c2.get()<<endl;return 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : heimao
cat info name : TomCat
cat info name : TomCat
address: 000001D2F5343170
Destructor of Cat
PS D:\Work\C++UserLesson\cppenv\bin\Debug>
链式调用
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;std::unique_ptr<Cat> get_unique_ptr() {std::unique_ptr<Cat> p_cat = std::make_unique<Cat>("local cat");p_cat->catInfo(); // 输出: local catcout<<p_cat.get()<<endl;cout<<&p_cat<<endl;p_cat->set_cat_name("TomCat");p_cat->catInfo(); // 输出: TomCatreturn p_cat;
}int main(int argc,char* argv[]) {get_unique_ptr()->catInfo();cout<<"over~"<<endl;return 0;
}
运行结果:
PS D:\Work\C++UserLesson\cppenv\bin\Debug> ."D:/Work/C++UserLesson/cppenv/bin/Debug/app.exe"
Constructor of Cat : local cat
cat info name : local cat
0000023AC98439B0
00000077859AF7C8
cat info name : TomCat
cat info name : TomCat
Destructor of Cat
over~
PS D:\Work\C++UserLesson\cppenv\bin\Debug>