推荐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(); //ruite
u_c_p2->catInfo(); //ruite
c_p2->set_cat_name("lanmao");
u_c_p2->catInfo(); //lanmao
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
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[]) {
// 第二种 new
std::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_unique
std::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_unique
std::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 value
std::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 value
std::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")); //move
return 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 cat
cout<<p_cat.get()<<endl;
cout<<&p_cat<<endl;
p_cat->set_cat_name("TomCat");
p_cat->catInfo(); // 输出: TomCat
return 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>