本文已参与「新人创作礼」活动,一起开启掘金创作之路。
简介:
C++语言开发者在发展中不断优化其语法,增加更多的好用的语法糖,今天要给大家介绍的就是C++在11版本中增加的 智能指针
1、三种智能指针
-
std::shared_ptr; -
std::unique_ptr; -
std::weak_ptr;
备注: 在C++ 11 后 std::auto_ptr 已经被废弃了
2、智能指针的介绍
很多人都知道C/C++中的指针概念,一听到指针就很懵,指针里面的内容让人头皮发麻,不过放心,本文只讲比较专一的知识点,今天就重点看下智能指针的使用和特点。我们知道,C/C++自带的指针需要我们手动分配内存,手动释放,然后经常在开发的时候,很多开发人员会忘记释放申请的空间,从而导致了内存泄漏的问题,那么智能指针的出现就可以很好的避免这个问题,其实呀,智能指针就是对原始指针的封装,其会主动释放内存,不用担心内存泄漏,当然说到这里很多就有疑问,怎么自动释放内存呢,接下来我们一步步来分析。
但是补充一点,并不是所有的指针都可以封装成智能指正,而且在有些场合,有些业务条件下,原始指针比智能指针更加的方便
3、unique_ptr
- 1、从名字上来看,都知道这个指针唯一,其实是理解为,在某个时刻,这个指针是唯一管理一份内容空间的
- 2、与所有智能指针一样,当超出作用域的时候,指针指向的内存就会自动释放
- 3、该指针不能复制,智能移动
3.1、unique_ptr的创建
- 0、先看下原始指针
int main() {
// 1 std::shared_ptr;
// 2 std::unique_ptr;
// std::unique_ptr<Person> pp = std::unique_ptr<Person>();
// 3 std::weak_ptr;
Person* p1 = new Person("张三");
{
Person* p2 = new Person("李四");
}
return 0;
}
可以看到原始指针这种方式,并没有调用析构函数
int main() {
// 1 std::shared_ptr;
// 2 std::unique_ptr;
// std::unique_ptr<Person> pp = std::unique_ptr<Person>();
// 3 std::weak_ptr;
Person* p1 = new Person("张三");
{
Person* p2 = new Person("李四");
delete p2;
}
delete p1;
return 0;
}
- 1、通过原始指针的方式来创建
Person* p1 = new Person("张三");
//通过原始指针来创建unique_ptr
std::unique_ptr<Person> u_p1(p1);
//可以看到两者的内容一直
u_p1->printPerson();
p1->printPerson();
cout << "------------" << endl;
u_p1->editPersonName("孙悟空");
u_p1->printPerson();
p1->printPerson();
可以看到,通过原始指针来创建的独占指针,在转换后,两者都可以访问内存数据
- 2、通过new的形式来创建
std::unique_ptr<Person> new_ptr{new Person("newPtr")};
new_ptr->editPersonName("newPtrCreate");
new_ptr->printPerson();
可以看到这种方式也是很好用的,这里的Person对象使我创建的一个自定义的类
- 3、通过make_unique的形式来创建
//通过make_unique的方式创建智能指针
std::unique_ptr<Person> createPerson = std::make_unique<Person>();
createPerson->editPersonName("createName");
createPerson->printPerson();
3.2、unique_ptr的函数调用
1、通过传递值的方式
首先创建一个函数,打印这个智能指针下的内容
void though_by_value(std::unique_ptr<Person> p1) {
p1->printPerson();
}
将智能指针传递过去
std::unique_ptr<Person> createPerson = std::make_unique<Person>();
createPerson->editPersonName("test1");
createPerson->printPerson();
though_by_value(createPerson);
可以看到这里是报错了的,很明显我们上面提到过智能指针智能独占内存,不能 copy 只能 move
因为我们使用下面这种方式
std::unique_ptr<Person> createPerson = std::make_unique<Person>();
createPerson->editPersonName("test1");
createPerson->printPerson();
though_by_value(std::move(createPerson));
但是在这里提示一点,当我们使用move的时候,createPerson就不能再调用他的函数了,因为,已经使用move传递过去了,可以理解 createPerson指针已经失去了对内存的占有
2、通过传递引用的方式 首先创建一个接受智能指针引用的方式
/*
通过传递引用的方式
*/
void though_by_ref(std::unique_ptr<Person> &p2) {
p2->printPerson();
p2.reset();
}
在这里我们调用了 p2.reset(),当只能指针调用了reset的是,就不会再指向这个对象了,因此在下面的get地址函数打印当中我们可以看到,全部是0,当然在这里我们为了避免这种修改可以在函数的参数里面加上const,这样一来,就没办法调用reset
void though_by_ref(std::unique_ptr<Person> &p2) {
p2->printPerson();
p2.reset();
}
std::unique_ptr<Person> createPerson = std::make_unique<Person>();
createPerson->editPersonName("test2");
createPerson->printPerson();
though_by_ref(createPerson);
cout << "地址" << createPerson.get() << endl;
当不修改指针的指向时下面的createPerson就能打印出来地址
3、链式调用
在本地函数中创建一个智能指针,并将智能指针返回
std::unique_ptr<Person> get_unique_ptr() {
std::unique_ptr<Person> local_ptr = std::make_unique<Person>();
local_ptr->editPersonName("localName");
return local_ptr;
}
//链式调用
get_unique_ptr()->printPerson();
但是要注意,这种链式调用的地址在函数内的地址个get的地址是不一样的
std::unique_ptr<Person> get_unique_ptr() {
std::unique_ptr<Person> local_ptr = std::make_unique<Person>();
local_ptr->editPersonName("localName");
cout << "loacl address " << &local_ptr << endl;
cout << "loacl address get " << local_ptr.get() << endl;
return local_ptr;
}
4、shared_ptr
共享指针
//共享指针
//1、常量指针
std::shared_ptr<int> int_ptr = make_shared<int>(200);
cout << "value: " << *int_ptr << endl;
cout << "use cout:" << int_ptr.use_count() << endl;
std::shared_ptr<int> int_ptr2 = int_ptr;
cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
cout << "copy after int_ptr2 use cout:" << int_ptr2.use_count() << endl;
//一个指针改变,另外一个指针也会跟着改变
*int_ptr2 = 30;
cout << "value: " << *int_ptr << endl;
//现在来看两个指针的引用次数
std::shared_ptr<int> int_ptr3 = int_ptr;
cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
cout << "usd int_ptr2 cout:" << int_ptr2.use_count() << endl;
cout << "usd int_ptr3 cout:" << int_ptr3.use_count() << endl;
int_ptr = nullptr;
cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
cout << "usd int_ptr2 cout:" << int_ptr2.use_count() << endl;
cout << "usd int_ptr3 cout:" << int_ptr3.use_count() << endl;
共享指针明显与独占指针有一个很大的不同就是,共享指针可以有很多个指针指向同一块,内存,并可以获取use_count,但是需要注意,共享指针的内存只有一块,因此,在释放的时候,也只会释放一次,并且是当所有的use_count都为0的时候,才会释放。
1、值传递
首先我们创建一个共享指针的函数
void though_by_value_share(std::shared_ptr<Person> p1) {
p1->editPersonName("-hbhb");
cout << p1->getName() << endl;
cout << " use cout:" << p1.use_count() << endl;
}
在这个共享指针李我们修改名字,然后打印名字,再打印指针的引用次数
std::shared_ptr<Person> pp = make_shared<Person>();
pp->editPersonName("waibupp");
though_by_value_share(pp);
cout << "name:" << pp->getName() << endl;
cout << "外部use cout" << pp.use_count() << endl;
在外部同样我们打印指针的名字,也打印指针的引用次数
可以看到,在函数内部修改的指针内存空间,在外部同样是修改了的,但是在函数内部我们发现,函数的指针次数是2,在函数外部是1,因为在函数执行完毕后,会释放一份指针。
2、引用传递
void though_by_ref_share(std::shared_ptr<Person> &p2) {
p2->editPersonName("-hbhb");
cout << p2->getName() << endl;
cout << " use cout:" << p2.use_count() << endl;
}
通过引用的方式,将指针传递到函数体内
//引用传递
std::shared_ptr<Person> pp2 = make_shared<Person>();
though_by_ref_share(pp2);
cout <<"name:" << pp2->getName() << endl;
cout << "use cout" << pp2.use_count() << endl;
同样我们在函数体内外打印一些数据
可以看到,use_cout 的值在函数传递前后并没有改变,且函数体内的值在改变后,同样,函数外面的指针指向的值也会发生改变
5、shared_ptr 和 unique_ptr
shared_ptr 是不能转换成 unique_ptr,但是unique_ptr可以转换成shared_ptr
// std::unique_ptr<Person> pp3(std::move(pp2)); 不能这样转换
std::unique_ptr<Person> pp3 = make_unique<Person>();
std::shared_ptr<Person> pp4(std::move(pp3));
5、weak_ptr
weak_ptr是一种虚引用,其实它并不会对内存拥有占用的权限,因此,他无法使用-> 和*,weak_ptr的出现是为了解决循环依赖的问题,比如在一个类中拥有另一个类的引用,那么在释放的时候,就会出现无法直接释放的问题,这是一个经典的问题。因为我们通过weak_ptr来标记这个对象,在使用的时候,将其转换为其他指针,而在释放时,由于其并不会对内存占有引用,因此可以安全的释放,不必担心内存泄漏的风险。
1、use_cout 的引用问题
std::shared_ptr<Person> ps = std::make_shared<Person>();
std::weak_ptr<Person> weak_c(ps);
cout << "use cout" << ps.use_count() << endl;
cout << "weak use cout" << weak_c.use_count() << endl;
可以看到在使用weak_ptr后,use_cout并没有改变,因此可以说明weak_ptr只是一个标志,其并不会对内存进行占用
2、转换问题
std::shared_ptr<Person> ps = std::make_shared<Person>();
std::weak_ptr<Person> weak_c(ps);
cout << "use cout" << ps.use_count() << endl;
cout << "weak use cout" << weak_c.use_count() << endl;
std::shared_ptr<Person> ps2 = weak_c.lock();
cout << "shared_ptr use cout" << ps2.use_count() << endl;
从上面的例子来看,weak_ptr可以通过lock函数,将weak_ptr转换为shared_ptr,因此,转换后又的use_cout就会增加1
3、经典的循环引用问题
这个问题在开发过程中经常会出现,也就是在两个类中存在相互依赖的问题而导致不能释放的问题,首先我们设计一个Person的类
class Person {
public:
Person() {}
Person(string namestr) {
this->name = namestr;
cout << "Person的构造函数" << this->name <<endl;
}
~Person() {
cout << "Person的析构函数" << this->name<<endl;
}
void printPerson() {
cout <<"人员的名字为:" << this->name << endl;
}
string getName() {
return this->name;
}
void editPersonName(string newName) {
this->name = newName;
}
void setFriend(std::shared_ptr<Person> fri) {
this->mFriend = fri;
}
public:
string name = std::string("biaoge");
std::shared_ptr<Person> mFriend;
};
第一步我们首先创建简单的两个share_ptr
std::shared_ptr<Person> p1 = std::make_shared<Person>("p1");
std::shared_ptr<Person> p2 = std::make_shared<Person>("p2");
我们可以看到,直接创建后在执行完就会调用析构函数释放
第二步我们相互设置依赖
std::shared_ptr<Person> p1 = std::make_shared<Person>("p1");
std::shared_ptr<Person> p2 = std::make_shared<Person>("p2");
p1->setFriend(p2);
p2->setFriend(p1);
可以很明显的看到,没有调用析构函数,因为在释放的时候,相互依赖,计算机内存在不着急的情况下,如果有引用就不会释放。
第三步 使用weak_ptr
public:
string name = std::string("biaoge");
std::weak_ptr<Person> mFriend;
通过将Person的成员变量的类型改为weak_ptr,就会发现可以正常调用析构函数释放内存
6、全部代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <deque>
#include <list>
#include <queue>
#include <set>
#include <ctime>
#include <map>
#include <functional>
using namespace std;
//抽象人员
class Student {
public:
string name;
};
void printVector(vector<Student> vec) {
for (vector<Student>::iterator it = vec.begin(); it != vec.end(); it++) {
cout << (*it).name << endl;
}
}
//创建人员
void CreateStudent(queue<Student>& que, int num) {
string setName = "ABCDEFGHIJKLMNOPK";
int sum = rand() % 10;
for (int i = 0; i < sum; i++) {
Student stu;
char buf[64] = { 0 };
sprintf(buf,"%d",num);
string s(buf);
stu.name = "第";
stu.name += s;
stu.name += "层";
stu.name += setName[i];
que.push(stu);
}
}
//mylist是电梯 que是队列容器,也就是每一层的人数 pushV是拷贝进电梯的人员
int PushList(list<Student> &mylist,queue<Student> &que,vector<Student> &pushV) {
int tmppush = 0;//临时变量,记录出电梯的人员数
while (!que.empty()) {
if (mylist.size() > 15) {
break;
}
//从每一层容器中取出一个人
Student s = que.front();
//拷贝到层数统计的vector当中去
pushV.push_back(s);
//进电梯
mylist.push_back(s);
//删除队列中的当前数据
que.pop( );
tmppush++;
}
return tmppush;
}
//mylist 电梯 que出电梯的人员 num 层数
int PopList(list<Student>& mylist, vector<Student> &popV, int num) {
int temppop = 0;
if (num == 17) { //当到达17层,所有的人都要出电梯
while (!mylist.empty()) {
Student s = mylist.front();
//把出电梯的人拷贝到popV中
popV.push_back(s);
//删除电梯里的人
mylist.pop_front();
temppop++;
}
}
int n = rand() % 5;//随机出电梯的人数
if (n == 0) {
return temppop;
}
//当电梯有人,且随机人数大于等于电梯的人数,
if (mylist.size() > 0 && mylist.size() >= n) {
for (int i = 0; i < n; i++) {
Student stu = mylist.front();
popV.push_back(stu);
//删除电梯里的人
mylist.pop_front();
temppop++;
}
}
return temppop;
}
void test05() {
srand((unsigned int)time(NULL));
list<Student> mylist;//创建电梯容器
int Pushnum = 0;//进电梯的总人数
int Popnum = 0; // 出电梯的总人数
vector<Student> pushV; //记录进电梯的人员
vector<Student> popV; //记录出电梯的人员
//电梯上升
for (int i = 1; i < 18; i++) {
//创建人员
queue<Student> que;
//创建的人员在哪一层
CreateStudent(que, i);
//判断是否能进电梯
if (mylist.size() <= 15) {
//17层不用进电梯
if (i < 17) {
//进电梯
Pushnum += PushList(mylist, que, pushV);
}
}
//判断出电梯的条件
if (mylist.size() > 0) { //电梯有人才能出
if (i > 1) { //第一层,电梯是空的
//出电梯
Popnum += PopList(mylist, popV, i);
}
}
}
//打印进电梯的人员
printVector(pushV);
cout << "进电梯的人数" << Pushnum << endl;
//打印出电梯的人员
printVector(popV);
cout << "出电梯的人数" << Popnum << endl;
}
void test06() {
multiset<int> s;
s.insert(5);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(5);
s.insert(9);
//set<int>::iterator it = s.find(19);
//if (it == s.end()) {
// cout << "查找失败" << endl;
//}
//else {
// cout << "查找成功" << endl;
//}
cout << s.count(5) << endl;
}
#define SALE_DEPATMENT 1//销售部门
#define DEVELOP_DEPATMENT 2//研发部门
#define FINACIAL_DEPATMENT 3///财务部门
class Worker {
public:
string name;
int age;
int Salary;
};
void CreateWorker(vector<Worker> &work) {
srand((unsigned int)time(NULL));
string setName = "ABCDE";
for (int i = 0; i <5 ; i++) {
Worker worker;
worker.name = "员工";
worker.name += setName[i];
worker.age = rand() % 30 + 30;
worker.Salary = rand() % 20000 + 20000;
work.push_back(worker);
}
}
void WorkerByGroup(vector<Worker> &work, multimap<int, Worker> &mwork) {
//遍历员工
for (vector<Worker>::iterator it = work.begin(); it != work.end(); ++it) {
int id = rand() % 3 + 1;
//员工保存到mwork中
//mwork[]
mwork.insert(make_pair(id, *it));
}
}
void PrintWorker(multimap<int, Worker> &works) {
}
void test111() {
//保存未分组的员工信息
vector<Worker> vWorker;
//保存分组后员工的信息
multimap<int, Worker> mWokers;
//创建员工信息
CreateWorker(vWorker);
//把分好组的员工放入mWokers
WorkerByGroup(vWorker,mWokers);
//打印员工信息
PrintWorker(mWokers);
}
//第一步 继承binary_function<参数1,参数2,返回类型>
struct Myfunc:public binary_function<int,int,void>{
void operator()(int val,int vals) const{ //第二步: 加上const成为常函数,常函数其实就是为了防止对成员变量的修改
cout << val+ vals << endl;//第三步重写,函数体
}
};
void test007() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
v.push_back(60);
}
class Person {
public:
Person() {}
Person(string namestr) {
this->name = namestr;
cout << "Person的构造函数" << this->name <<endl;
}
~Person() {
cout << "Person的析构函数" << this->name<<endl;
}
void printPerson() {
cout <<"人员的名字为:" << this->name << endl;
}
string getName() {
return this->name;
}
void editPersonName(string newName) {
this->name = newName;
}
void setFriend(std::shared_ptr<Person> fri) {
this->mFriend = fri;
}
public:
string name = std::string("biaoge");
std::weak_ptr<Person> mFriend;
};
void though_by_value(std::unique_ptr<Person> p1) {
p1->printPerson();
}
/*
通过传递引用的方式
*/
void though_by_ref(const std::unique_ptr<Person> &p2) {
p2->printPerson();
}
std::unique_ptr<Person> get_unique_ptr() {
std::unique_ptr<Person> local_ptr = std::make_unique<Person>();
local_ptr->editPersonName("localName");
cout << "loacl address " << &local_ptr << endl;
cout << "loacl address get " << local_ptr.get() << endl;
return local_ptr;
}
void though_by_value_share(std::shared_ptr<Person> p1) {
p1->editPersonName("-hbhb");
cout << p1->getName() << endl;
cout << " use cout:" << p1.use_count() << endl;
}
void though_by_ref_share(std::shared_ptr<Person> &p2) {
p2->editPersonName("-hbhb");
cout << p2->getName() << endl;
cout << " use cout:" << p2.use_count() << endl;
}
int main() {
// 1 std::shared_ptr;
// 2 std::unique_ptr;
// std::unique_ptr<Person> pp = std::unique_ptr<Person>();
// 3 std::weak_ptr;
//Person* p1 = new Person("张三");
//通过原始指针来创建unique_ptr
//std::unique_ptr<Person> u_p1(p1);
////可以看到两者的内容一直
//u_p1->printPerson();
//p1->printPerson();
//cout << "------------" << endl;
//u_p1->editPersonName("孙悟空");
//u_p1->printPerson();
//p1->printPerson();
//通过new的方式来创建智能指针
/*std::unique_ptr<Person> new_ptr{new Person("newPtr")};
new_ptr->editPersonName("newPtrCreate");
new_ptr->printPerson();*/
//通过make_unique的方式创建智能指针
//std::unique_ptr<Person> createPerson = std::make_unique<Person>();
//createPerson->editPersonName("test1");
//createPerson->printPerson();
//std::unique_ptr<Person> createPerson = std::make_unique<Person>();
//createPerson->editPersonName("test1");
//createPerson->printPerson();
//though_by_value(std::move(createPerson));
//std::unique_ptr<Person> createPerson = std::make_unique<Person>();
//createPerson->editPersonName("test2");
//createPerson->printPerson();
//though_by_ref(createPerson);
//cout << "地址" << createPerson.get() << endl;
//链式调用
//get_unique_ptr()->printPerson();
//共享指针
//1、常量指针
//std::shared_ptr<int> int_ptr = make_shared<int>(200);
//cout << "value: " << *int_ptr << endl;
//cout << "use cout:" << int_ptr.use_count() << endl;
//std::shared_ptr<int> int_ptr2 = int_ptr;
//cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
//cout << "copy after int_ptr2 use cout:" << int_ptr2.use_count() << endl;
////一个指针改变,另外一个指针也会跟着改变
//*int_ptr2 = 30;
//cout << "value: " << *int_ptr << endl;
////现在来看两个指针的引用次数
//std::shared_ptr<int> int_ptr3 = int_ptr;
//cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
//cout << "usd int_ptr2 cout:" << int_ptr2.use_count() << endl;
//cout << "usd int_ptr3 cout:" << int_ptr3.use_count() << endl;
//int_ptr = nullptr;
//cout << "use int_ptr cout:" << int_ptr.use_count() << endl;
//cout << "usd int_ptr2 cout:" << int_ptr2.use_count() << endl;
//cout << "usd int_ptr3 cout:" << int_ptr3.use_count() << endl;
//值传递
//std::shared_ptr<Person> pp = make_shared<Person>();
//pp->editPersonName("waibupp");
//though_by_value_share(pp);
//cout << "name:" << pp->getName() << endl;
//cout << "外部use cout" << pp.use_count() << endl;
//引用传递
//std::shared_ptr<Person> pp2 = make_shared<Person>();
//though_by_ref_share(pp2);
//cout <<"name:" << pp2->getName() << endl;
//cout << "use cout" << pp2.use_count() << endl;
//
//// std::unique_ptr<Person> pp3(std::move(pp2)); 不能这样转换
//std::unique_ptr<Person> pp3 = make_unique<Person>();
//std::shared_ptr<Person> pp4(std::move(pp3));
//std::move(pp2);
//std::shared_ptr<Person> ps = std::make_shared<Person>();
//std::weak_ptr<Person> weak_c(ps);
//cout << "use cout" << ps.use_count() << endl;
//cout << "weak use cout" << weak_c.use_count() << endl;
//std::shared_ptr<Person> ps2 = weak_c.lock();
//cout << "shared_ptr use cout" << ps2.use_count() << endl;
std::shared_ptr<Person> p1 = std::make_shared<Person>("p1");
std::shared_ptr<Person> p2 = std::make_shared<Person>("p2");
p1->setFriend(p2);
p2->setFriend(p1);
return 0;
}