前言
unique_ptr采用独享语义,在任何给定时刻,只能有一个指针管理内存。当指针超出作用域时,内存将自动释放,而且该类型的指针不可copy,只可以move。
一、测试:栈
在使用unique_ptr前,先用类对象对栈内存进行测试。
1.类的头文件声明如下:
#ifndef CAT_H
#define CAT_H
#include<string>
#include<iostream>
class Cat
{
public:
Cat(std::string name);
Cat()=default;
~Cat();
void cat_info() const
{
std::cout<<"cat info name:"<<name<<std::endl;
}
std::string get_name() const
{
return name;
}
void set_cat_name(const std::string &name)
{
this->name=name;
}
private:
std::string name{"Mimi"};
};
#endif
2.头文件对应的cpp:
#include "cat.h"
Cat::Cat(std::string name):name(name)
{
std::cout<<"Constructor of Cat:"<<name<<std::endl;
}
Cat::~Cat()
{
std::cout<<"Destructor of Cat"<<std::endl;
}
3.主函数:
#include<iostream>
#include<memory>//使用智能指针的头文件
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//stack
Cat c1("OK");
c1.cat_info();
{
Cat c1("OK");
c1.cat_info();
}
return 0;
}
运行结果:
结论:在栈分配的局部变量生命周期结束后自动释放
二、原始指针
1.使用原始指针构建对象
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//heap
//raw pointer
Cat *c_p1=new Cat("yy");
c_p1->cat_info();
{
Cat *c_p1=new Cat("yy");
c_p1->cat_info();
}
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
以下用来验证局部指针释放后不影响外部指针:
Cat *c_p1=new Cat("yy");
c_p1->cat_info();
{
Cat *c_p1=new Cat("yy_scope");
c_p1->cat_info();
delete c_p1;
}
delete c_p1;
cout<<"----- yz -----"<<endl;
return 0;
运行结果:
结论:在堆上分配的局部变量生命周期结束后不会自动释放,需要程序员手动释放
2.使用原始指针会出现安全性问题:
Cat *c_p1=new Cat("yy");
c_p1->cat_info();
{
c_p1=new Cat("yy_scope");
c_p1->cat_info();
delete c_p1;
}
delete c_p1;
cout<<"----- yz -----"<<endl;
return 0;
运行结果:
结论:对同一个指针指向的内存释放两遍会引起程序的崩溃
三、unique_ptr
使用unique_ptr可以避免原始指针的安全性问题,它有三种使用方法
1.第一种方法:通过已有的裸指针创建
//unique_pointer 的三种创建方式
//第一种
Cat *c_p2=new Cat("yz");
std::unique_ptr<Cat> u_c_p2{c_p2};
// c_p2还能用吗?不能!建议销毁,否则如下,就不是独享语义了
/*-------------------------------*/
c_p2->cat_info();
u_c_p2->cat_info();
c_p2->set_cat_name("ok");
u_c_p2->cat_info();
/*-------------------------------*/
c_p2=nullptr;
delete c_p2;
u_c_p2->cat_info();
cout<<"----- yz -----"<<endl;
return 0;
运行结果:
c_p2->cat_info();
u_c_p2->cat_info();
c_p2->set_cat_name("ok");
u_c_p2->cat_info();
cout<<c_p2<<endl;
cout<<u_c_p2.get()<<endl;
运行结果:
结论:第一种方法容易出现独享语义冲突的问题。
2.第二种方法 new
//第二种 用new
std::unique_ptr<Cat> u_c_p3{new Cat("dd")};
u_c_p3->cat_info();
u_c_p3->set_cat_name("oo");
u_c_p3->cat_info();
cout<<"----- yz -----"<<endl;
运行结果:
3.第三种方法 make_unique(建议使用第三种方法)
//第三种 std::make_unique
std::unique_ptr<Cat> u_c_p4=make_unique<Cat>();
u_c_p4->cat_info();
u_c_p4->set_cat_name("oo");
u_c_p4->cat_info();
cout<<"----- yz -----"<<endl;
运行结果:
结论:使用智能指针后系统可以自动回收内存,避免内存泄漏问题。
4.补充:用unique指针创建int对象
//第二种 用new
std::unique_ptr<int> u_i_p3{new int(100)};
cout<<"int address:"<<u_i_p3.get()<<endl;//打印地址
cout<<*u_i_p3<<endl;
//第三种 std::mak_unique
std::unique_ptr<int> u_i_p4=make_unique<int>(200);
cout<<"int address:"<<u_i_p4.get()<<endl;//打印地址
cout<<*u_i_p4<<endl;
运行结果:
四、unique_ptr与函数调用
1.passing by value
1.1需要用move来转移内存拥有权。
unique_ptr采用独享语义,不能同时存在两个指向同一个对象的指针,如果在函数调用时不使用move的话会使程序报错。
下面是使用了move后的代码:
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
void do_with_cat_pass_value(std::unique_ptr<Cat> c)
{
c->cat_info();
}
int main(int argc, char *argv[])
{
std::unique_ptr<Cat> c1=make_unique<Cat>("ff");
do_with_cat_pass_value(move(c1));
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
以下验证:对上面的代码稍作修改,用来验证使用了move后,原来的指针c1失效:
int main(int argc, char *argv[])
{
//1.pass by value
std::unique_ptr<Cat> c1=make_unique<Cat>("ff");
do_with_cat_pass_value(move(c1));
c1->cat_info();
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
1.2如果参数直接传入std::make_unique语句,自动转换为move
int main(int argc, char *argv[])
{
//1.pass by value
std::unique_ptr<Cat> c1=make_unique<Cat>("ff");
do_with_cat_pass_value(move(c1));
do_with_cat_pass_value(make_unique<Cat>());//自动move
cout<<"----- yz -----"<<endl;
return 0;
运行结果:
2.pass by reference(引用)
2.1没加const
void do_with_cat_pass_ref(std::unique_ptr<Cat> &c)
{
c->set_cat_name("oo");
c->cat_info();
c.reset();
}
int main(int argc, char *argv[])
{
//2.pass by ref
unique_ptr<Cat> c2=make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
c2->cat_info();
cout<<"address"<<c2.get()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
以下验证:在函数里边释放指针后原来指针失效。
int main(int argc, char *argv[])
{
//2.pass by ref
unique_ptr<Cat> c2=make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
//c2->cat_info();
cout<<"address"<<c2.get()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
void do_with_cat_pass_ref(std::unique_ptr<Cat> &c)
{
c->set_cat_name("oo");
c->cat_info();
}
int main(int argc, char *argv[])
{
//2.pass by ref
unique_ptr<Cat> c2=make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
c2->cat_info();
cout<<"address"<<c2.get()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
结论:在函数里面释放后c2地址为0;如果在函数调用里面没有释放,c2地址不为0。
2.2加const,此时不允话修改指向了
void do_with_cat_pass_ref(const std::unique_ptr<Cat> &c)
{
c->set_cat_name("oo");
c->cat_info();
//c.reset();//不允话reset
}
int main(int argc, char *argv[])
{
//2.pass by ref
unique_ptr<Cat> c2=make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
c2->cat_info();
cout<<"address"<<c2.get()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
3.pass by return value
指向一个local object,可以用作链式函数
std::unique_ptr<Cat> get_unique_ptr()
{
std::unique_ptr<Cat> p_dog=std::make_unique<Cat>("Local cat");
return p_dog;
}
int main(int argc, char *argv[])
{
//链式
get_unique_ptr()->cat_info();
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
cout<<"unique address:"<<p_dog.get()<<endl;
cout<<"unique address:"<<&p_dog<<endl;
运行结果:
为什么地址返回的不一样,因为get返回的是指针指向的内存的地址,&返回的是指针的地址。
补充实验:
std::unique_ptr<Cat> get_unique_ptr(string name)
{
std::unique_ptr<Cat> p_dog=std::make_unique<Cat>(name);
cout<<"unique address:"<<p_dog.get()<<endl;
cout<<"unique address:"<<&p_dog<<endl;
return p_dog;
}
int main(int argc, char *argv[])
{
//链式
unique_ptr<Cat> p_dog1=get_unique_ptr("dog1");
unique_ptr<Cat> p_dog2=get_unique_ptr("dog2");
p_dog1->cat_info();
p_dog2->cat_info();
cout<<"----- yz -----"<<endl;
return 0;
}
运行结果:
总结
以上就是unique_ptr的用法。
这是本菜看大佬视频做的笔记,参考链接:C++现代实用教程:智能指针