C++现代实用教程:智能指针
课程地址:https://www.bilibili.com/video/BV18B4y187uL
在C++11中通过引入智能指针的概念,使得C++程序员不需要手动释放内存
智能指针的种类
std::unique_ptr
std::shared_ptr
std::weak_ptr
注: std::auto_ptr 已被遗弃
目录
1:智能指针概述
2:独占指针: unique_ptr
3:unique_ptr与函数
4:计数指针: shared_ptr
5:shareed_ptr与函数
6:shared_ptr与unique_ptr的转化
7:weak_ptr: shared_ptr的补充
第一节: 智能指针概述
c++的指针包括两种
- 原始指针(raw pointer)
- 智能指针
智能指针是原始指针的封装,其优点是会自动分配内存,不用担心潜在的内存泄露.
并不是所有的指针都可以封装成智能指针,很多时候原始指针要更方便
各种指针中,最常用的是裸指针,其次是unique_ptr和shared_ptr
weak_ptr是shared_ptr的一个补充,应用场景较少
智能指针与Rust的内存安全
智能指针只解决一部分问题,即独占/共享所有权指针的释放、传输.
智能指针没有从根本上解决C++内存安全问题,不加以注意依然会造成内存安全问题
第二节:独占指针: unique_ptr
unique_ptr
在任何给定的时刻,只能有一个指针管理内存
当指针超出作用域时,内存将自动释放
该类型指针不可Copy,只可以Move
三种创建方式
- 通过已有裸指针创建
- 通过new来创建
- 通过std::make_unique创建(推荐)
unique_ptr可以通过get()获取地址
unique_ptr实现了->与*
- 可以通过->调用成员函数
- 可以通过*调用dereferencing
例子
- 裸指针与unique_ptr的不同
- 创建的三种方式
- get()与->和解引用
//
// main.cpp
// c1_unique_pointer
//
// Created by ONEE on 2023/2/17.
//
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
int main(int argc, const char * argv[]) {
//stack
// Cat c1("OK");
// c1.cat_info();
// {
// Cat c1("OK");
// c1.cat_info();
// }
//heap raw pointer
// Cat *c_p1=new Cat("yy");
// int *i_p1=new int(100);
// cout<<*i_p1<<endl;
// c_p1->cat_info();
// {
//
// int *i_p1=new int(200);
// Cat *c_p1=new Cat("yy");
// c_p1->cat_info();
// delete c_p1;
// delete i_p1;
// }
// cout<<*i_p1<<endl;
// delete i_p1;//重复delete 会崩溃
//
// delete c_p1;
//
// Cat *c_p2=new Cat("yz");
// unique_ptr<Cat> u_c_p2(c_p2);
// c_p2->cat_info();
// u_c_p2->cat_info();
// c_p2->set_cat_name("aa");
// u_c_p2->cat_info();
// delete c_p2;
// c_p2=nullptr;
// u_c_p2->cat_info();
// unique_ptr<Cat> u_c_p3{new Cat("dd")};
// u_c_p3->cat_info();
// u_c_p3->set_cat_name("aa");
// u_c_p3->cat_info();
//
// //推荐方式
// unique_ptr<Cat> u_c_p4=make_unique<Cat>();
// u_c_p4->cat_info();
// u_c_p4->set_cat_name("bb");
// u_c_p4->cat_info();
unique_ptr<int> u_i_p3(new int(100));
unique_ptr<int> u_i_p4=make_unique<int>(200);
cout<<*u_i_p4<<endl;
cout<<"int address: "<<u_i_p3.get() << endl;
//std::cout << "Hello, World!\n";
return 0;
}
第三节:unique_ptr与函数调用
-
unique_ptr是不可Copy,只可以Move
-
在做函数参数或是返回值中一定要注意所有权
-
Passing by value
需要用std::move来转移内存拥有权
如果参数直接传入std::make_unique语句 自动转换为move
-
Passing by reference
如果设置参数为const则不能改变指向
比方说reset()
reset()方法为智能指针清空方法 -
Return by value
指向一个local object
可以用作链式函数
//
// main.cpp
// c2_unique_pointer_func
//
// Created by ONEE on 2023/2/17.
//
#include <memory>
#include "cat.h"
#include <iostream>
using namespace std;
void do_with_cat_pass_value(unique_ptr<Cat> c){
c->cat_info();
}
void do_with_cat_pass_ref(const unique_ptr<Cat> &c){
c->set_cat_name("ref");
c->cat_info();
// c.reset();
}
unique_ptr<Cat> get_unique_ptr(){
unique_ptr<Cat> p_dog=make_unique<Cat>("Local dog");
cout<<"unique addredd: "<<p_dog.get()<<endl;
cout<<"unique addredd: "<<&p_dog<<endl;
return p_dog;
}
int main(int argc, const char * argv[]) {
unique_ptr<Cat> c1=make_unique<Cat>("ff");
do_with_cat_pass_value(move(c1));
// c1->cat_info();
//make_unique
do_with_cat_pass_value(make_unique<Cat>());
//2 pass ref
//不加const
unique_ptr<Cat> c2=make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
//c2->cat_info();
cout<<"cat address: "<<c2.get()<<endl;
c2->cat_info();
//链式
cout<<"--------------"<<endl;
get_unique_ptr()->cat_info();
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~"<<endl;;
return 0;
}
第四节:计数指针: shared_ptr
- shared_ptr 计数指针又称共享指针
- 与unique_ptr不同的是它是可以共享数据的
- shared_ptr创建了一个计数器与类对象所指的内存相关联
- Copy则计数器加一,销毁则计数器减一
- api为use_count()
常量类型的shared_ptr
自定义类型的shared_ptr
//
// main.cpp
// c3_shared_pointer
//
// Created by ONEE on 2023/2/17.
//
#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;
int main(int argc, const char * argv[]) {
//常量类型
shared_ptr<int> i_p1=make_shared<int>(10);
//shared_ptr<int> i_p1=make_shared<int>{new int(10};
cout<<"value : "<<*i_p1<<endl;
cout<<"use count : "<<i_p1.use_count()<<endl;
//copy
shared_ptr<int> i_p2=i_p1;
cout<<"p1 use count : "<<i_p1.use_count()<<endl;
cout<<"p2 use count : "<<i_p2.use_count()<<endl;
*i_p2=30;
cout<<"i_p1 value : "<< *i_p1<<endl;
// i_p2=nullptr;
shared_ptr<int> i_p3=i_p1;
i_p1=nullptr;
cout<<"p1 use count : "<<i_p1.use_count()<<endl;
cout<<"p2 use count : "<<i_p2.use_count()<<endl;
cout<<"p3 use count : "<<i_p3.use_count()<<endl;
//自定义类型
shared_ptr<Cat> c_p1=make_shared<Cat>();
shared_ptr<Cat> c_p2=c_p1;
shared_ptr<Cat> c_p3=c_p1;
cout<<"c_p1 usecount : "<<c_p1.use_count()<<endl;
cout<<"c_p2 usecount : "<<c_p2.use_count()<<endl;
cout<<"c_p3 usecount : "<<c_p3.use_count()<<endl;
c_p1.reset();
cout<<"c_p1 usecount : "<<c_p1.use_count()<<endl;
cout<<"c_p2 usecount : "<<c_p2.use_count()<<endl;
cout<<"c_p3 usecount : "<<c_p3.use_count()<<endl;
std::cout << "Hhhhhhhhhhhhhhhhhh\n";
return 0;
}
第五节: shared_ptr与函数
- shared_ptr passed by value
copy
函数内部计数器加一 - shared_ptr passed by ref
const表示不可改变指向 - returning by value
链式调用
//
// main.cpp
// c4_shared_pointer_func
//
// Created by ONEE on 2023/2/17.
//
#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;
void cat_by_value(shared_ptr<Cat> cat){
cout<<cat->get_name()<<endl;
cat->set_cat_name("value");
cout<<"func use count : "<<cat.use_count()<<endl;
}
void cat_by_ref(shared_ptr<Cat> cat){
cout<<cat->get_name()<<endl;
cat->set_cat_name("value");
cat.reset(new Cat());
cout<<"func use count : "<<cat.use_count()<<endl;
}
shared_ptr<Cat> get_shared_ptr(){
shared_ptr<Cat> cat_p=make_shared<Cat>("local cat");
return cat_p;
}
int main(int argc, const char * argv[]) {
// insert code here...
shared_ptr<Cat> c1 = make_shared<Cat>("dd");
cat_by_value(c1);
c1->cat_info();
cout<<c1.use_count()<<endl;
cout<<"~~~~~~~~~~~~~`\n";
cat_by_ref(c1);
c1->cat_info();
std::cout << "Hello, World!\n";
cout<<"~~~~~~~~~~~~~~~`\n";
shared_ptr<Cat> c_p=get_shared_ptr();
c_p->cat_info();
return 0;
}
第六节: shared_ptr与unique_ptr
-
不能将shared_ptr转换为unique_ptr
-
unique_ptr可以转换为shared_ptr
通过std::move
常见的设计
将你的函数返回unique_ptr是一种常见的设计模式,这样可以提高代码的复用度,你可以随时改变为shared_ptr
unuque_ptr可以直接赋值给shared_ptr类型,可以随时改变返回类型为shared_ptr
//
// main.cpp
// c5_shared_unique
//
// Created by ONEE on 2023/2/17.
//
#include <iostream>
#include <memory>
#include "cat.h"
using namespace std;
unique_ptr<Cat> get_unique_ptr(){
unique_ptr<Cat> cat_p=make_unique<Cat>("local cat");
return cat_p;
}
int main(int argc, const char * argv[]) {
unique_ptr<Cat> c_p1=make_unique<Cat>("dd");
shared_ptr<Cat> c_p2=move(c_p1);
cout<<"c_p2 use count : "<< c_p2.use_count()<<endl;
shared_ptr c_p3=get_unique_ptr();
if(c_p3){
c_p3->cat_info();
cout<<"c_p3 use count : "<<c_p3.use_count()<<endl;
}
// insert code here...
std::cout << "Hello, World!\n";
return 0;
}
第七节:weak_ptr
weak_ptr并不拥有所有权
并不能调用 -> 和 解引用*
weak_ptr为什么会存在呢
- A类中有一个需求需要存储其他A类对象的信息
- 如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(Cyclic dependency problem)
- 所以我们这里需要用一个不需要拥有所有权的指针来标记该同类对象
weak_ptr可以通过lock()函数来提升为shared_ptr(类型装换)
//
// main.cpp
// c6_weak_pointer
//
// Created by ONEE on 2023/2/17.
//
#include <iostream>
#include "cat.h"
#include <memory>
using namespace std;
int main(int argc, const char * argv[]) {
shared_ptr<Cat> s_pc1=make_shared<Cat>("c1");
weak_ptr<Cat> w_pc1(s_pc1);
shared_ptr<Cat> s_pc2=s_pc1;
cout<<"w_pc1 :" <<w_pc1.use_count()<<endl;
cout<<"s_pc1 :" <<s_pc1.use_count()<<endl;
// insert code here...
shared_ptr<Cat> s_pc3=w_pc1.lock(); //lock 使得weakptr转化为sharedptr
cout<<"w_pc1 :" <<w_pc1.use_count()<<endl;
cout<<"s_pc1 :" <<s_pc1.use_count()<<endl;
cout<<"s_pc2 :" <<s_pc2.use_count()<<endl;
cout<<"s_pc3 :" <<s_pc3.use_count()<<endl;
cout<<"~~~~~~~~~~~~~`"<<endl;
shared_ptr<Cat> c3=make_shared<Cat>("c3");
shared_ptr<Cat> c4=make_shared<Cat>("c4");
c3->set_friend(c4);//销毁不了c3 c4 把shared改成weak
c4->set_friend(c3);
std::cout << "Hello, World!\n";
return 0;
}
cat.h
#ifndef CAT_H
#define CAT_H
#include<string>
#include<iostream>
class Cat{
private:
std::string name=("MOren");
std::weak_ptr<Cat> m_friend;
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;
}
void set_friend(std::shared_ptr<Cat> c){
m_friend=c;
}
};
#endif
cat.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 :"<< name << std::endl;
}