unique_ptr的使用
文章目录
- 前言
- 一、测试:栈
- 二、原始指针
- 三、unique_ptr
- 四、unique_ptr与函数调用
- 总结
前言
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++现代实用教程:智能指针
m0_52165668: 请问BC融合算法的Pij怎么求啊
做而论道_CS: 一个补码,它代表的十进制数,是多少? 也不难求。 你只需记住:【补码首位的权,是负数】。 一般的八位二进制数,各个位的权是: 128、64、32、16、8、4、2、1; 如果是八位的补码,各个位的权则是: -128、64、32、16、8、4、2、1。 例如,有一个补码:1110 0001, 它代表的十进制是:-128 + 64 + 32 + 1 = -31。 如果,另一个补码:0110 0001, 它代表的十进制是:0 + 64 + 32 + 1 = +97。 仅仅使用【进制转换】,不就完事了! ---------------------- 正负数值与其 “补码”,可以直接互相转换。 并不需要什么 “符号位原码反码取反加一”。 看过《卖拐》吧? 知道谁是 “大忽悠” 吧? 其实,计算机专家和老师,才是真正的大忽悠! 东北的老赵,远远跟不上他们! 学完了计算机,你就看不见老赵了!
做而论道_CS: +99、+255,都可以当做-1 ! 为什么呢? 绝对不是因为 “符号位原码反码补码 ... ” 。 而是你【舍弃了进位】! 也就是:减去了进位。 进位是多少? 两位十进制,进位就是:10^2 = 100; 八位二进制,进位就是:2^8 = 256。 那么: 加 99,就是:+99-100 = -1。 加 255,就是:+255-256 = -1。 加 254,就是:+254-256 = -2。 加 253,就是:+253-256 = -3。 。。。 这些个正数,都可以当做负数使用。 其原因,不过就是一个小学的算术题而已。 计算机专家,由此就 “发明” 了补码! 真不要 FACE 了。 ------------- 那么,负数的 “补码”,应该怎么求呢? 你一定能总结出规律: 负数的补码 = 256 + 该负数。 如,-38 存入计算机,是什么样子? 解:-38 + 256 = 218, 218 = 1101 1010 (二进制)。 这就是-38 的 “补码”。 求补码,并不需要掌握: 机器数真值符号位原码反码补码正数三码相同 负数取反加一符号位不变模符号位也参加运算 ... 这些,就是全背下来,也不能理解: 加法,怎么就成了减法呢? ------------- 另外,正数的 “补码”,又该怎么求呢? 也用同样的公式: 正数的补码 = 256 + 该正数。 加上 256,就会出现进位!那就舍弃了吧。 于是,公式就简化为: 正数的补码 = 该正数。 这就证明了: 零和正数的补码,就是其本身。 ------------- 老外的数学水平太洼,算术能力不行。 计算二进制数,手指头,更是不好用了。 实在算不出来,所以,才编造了一大批谎言。 我们的老师的数学水平,倒也不见得很差。 但是,盲目的迷信与崇外,也就只会跟疯了。 拿小学知识唬弄大学生,甚至还成了考研内容? 真是毁人不倦坑人不浅!
做而论道_CS: 虽然,计算机,使用二进制数。 但是,二进制数,也是数,并不是什么什么码。 如果用数字表示其它信息,才能叫做“码”。 如:学号门牌车牌电话性别英文汉字声音图像颜色... 实际上,原码反码取反加一...,都是不存在的。 所谓的 “补码”,也是一个简单算术题目而已。 补码,对任何进制,都是适用的。 你看十进制数,两位,真值是:0 ~99。 这个 +99,就可以当做 “负一” 使用。 如:27 + 99 = (一百) 26 27 - 1 = 26 只要你舍弃进位,依旧保持两位数: 减法,就能用加法来实现。 正数,也就能当成负数来使用。 在计算机中舍弃进位,就可以省掉减法器了。 舍弃进位意义就是:简化计算机的硬件。 ----------------- 计算机的字长,是固定的。 八位机,它只会做: 八位 + 八位 = 一个进位、八位的和。 八位二进制,真值是:0000 0000 ~ 1111 1111。 换算到十进制,就是:0 ~ 255。 这个 255,就可以当做-1 使用。 如 27 - 1 = 26,八位机的算法如下: 0001 1011 + 1111 1111 (即 255) --------- (进 1 ) 0001 1010 你舍弃进位,取八位的结果,就是 26。 于是,你就用加法器,实现了减法运算!
-StarsTwinkle-: 听不懂,思....... [code=cpp] #include<bits/stdc++.h> [/code]