在C++11中通过引入智能指针的概念,使得C++程序员不需要手动释放内存。
注意:std::auto_ptr已被废弃
C++的指针包括两种
智能指针是对原始指针的封装,其优点是自动分配内存,不用担心潜在的内存泄漏。并不是所有的指针都可以封装成智能指针,很多时候原始指针要更方便。
各种指针中,最常用的是裸指针(原始指针),其次是unique_ptr和shared_ptr,weak_ptr是shared_ptr的一个补充,应用场景较少。
智能指针与Rust的内存安全
智能指针只解决一部分问题,即独占/共享所有权指针的释放、传输。但智能指针没有从根本上解决C++内存安全问题,不加以注意依然会造成内存安全问题。
unique_ptr在任何给定的时刻,只能有一个指针管理内存,当指针超出作用域时,内存将自动释放,该类型指针不可Copy,只可以Move。
三种创建方式:
unique_ptr可以通过get()获取地址
unique_ptr实现了->与*
cat.h
#ifndef POINTER_CAT_H
#define POINTER_CAT_H
#include
#include
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()
{
return name;
}
void set_name(const std::string &name)
{
this->name = name;
}
private:
std::string name;
};
#endif //POINTER_CAT_H
cat.cpp
#include "cat.h"
Cat::Cat(std::string name):name(name)
{
std::cout << "Construct of Cat: " << name << std::endl;
}
Cat::~Cat()
{
std::cout << "Destructor of Cat: " << name << std::endl;
}
不加指针 main.cpp
#include "cat.h"
#include
using namespace std;
int main(int argc, char *argv[])
{
// stack
Cat c("OK");
c.cat_info();
{
Cat c("OK");
c.cat_info();
}
cout << "ending......" << endl;
return 0;
}
输出
Construct of Cat: OK
cat info name: OK
Construct of Cat: OK
cat info name: OK
Destructor of Cat: OK //局部作用域结束就会自动析构
ending......
Destructor of Cat: OK
原始指针main.cpp
#include "cat.h"
#include
using namespace std;
int main(int argc, char *argv[])
{
Cat *c_p1 = new Cat("kiki");
c_p1->cat_info();
{
Cat *c_p1 = new Cat("gigi");
c_p1->cat_info();
}
cout << "ending......" << endl;
return 0;
}
Construct of Cat: kiki
cat info name: kiki
Construct of Cat: gigi
cat info name: gigi
ending......
堆上的空间不会自动释放,需要手动调用delete
#include "cat.h"
#include
using namespace std;
int main(int argc, char *argv[])
{
Cat *c_p1 = new Cat("kiki");
c_p1->cat_info();
{
Cat *c_p1 = new Cat("gigi");
c_p1->cat_info();
delete c_p1;
}
delete c_p1;
cout << "ending......" << endl;
return 0;
}
Construct of Cat: kiki
cat info name: kiki
Construct of Cat: gigi
cat info name: gigi
Destructor of Cat: gigi
Destructor of Cat: kiki
ending......
上面是自定义类型,下面换成系统提供的类型
int main(int argc, char *argv[])
{
int *i = new int(100);
{
int *i = new int(200);
}
cout << *i << endl;
cout << "ending......" << endl;
return 0;
}
100
ending......
修改一下
int main(int argc, char *argv[])
{
int *i = new int(100);
{
i = new int(200);
}
cout << *i << endl;
cout << "ending......" << endl;
return 0;
}
200
ending......
再修改一下
int main(int argc, char *argv[])
{
int *i = new int(100);
{
i = new int(200);
delete i;
}
cout << *i << endl;
delete i;
cout << "ending......" << endl;
return 0;
}
pointer(1070,0x104bdc580) malloc: *** error for object 0x600003f20040: pointer being freed was not allocated
程序报错,上述代码存在的问题是第二次delete,并且第一次开辟的空间100没有释放,造成内存泄漏。也就是说,在使用原始指针时,每次new都需要delete,忘记delete或者额外的delete都可能造成程序错误,所以是不安全不方便的。
Cat *c_p = new Cat("kiki");
std::unique_ptr<Cat> u_c_p{c_p};
c_p->cat_info(); // cat info name: kiki
u_c_p->cat_info(); // cat info name: kiki
// 当使用原始指针改变对象时,独占指针也会随之改变
// 这样就不满足"独占"的含义
c_p->set_name("haha");
u_c_p->cat_info(); // cat info name: haha
// 建议销毁原始指针
c_p = nullptr;
delete c_p;
u_c_p->cat_info(); // cat info name: haha
cout << "ending......" << endl;
std::unique_ptr<Cat> u_ptr{new Cat("dd")};
u_ptr->cat_info();
u_ptr->set_name("haha");
u_ptr->cat_info();
cout << "ending......" << endl;
这种方式只有c++14才支持
std::unique_ptr<Cat> u_ptr = make_unqiue<Cat>();
// std::unique_ptr u_i = make_unique(100);
// cout << u_i.get() << endl;
u_ptr->cat_info();
u_ptr->set_name("haha");
u_ptr->cat_info();
以上三种方式都会自动调用析构函数
unique_ptr是不可copy,只可以move,在做函数参数或者返回值中一定要注意所有权。
void do_with_cat_pass_value(std::unique_ptr<Cat> c)
{
c->cat_info();
}
std::unique_ptr<Cat> c{new Cat("kiki")};
do_with_cat_pass_value(std::move(c));
// do_with_cat_pass_value(std::make_unique()); // 隐式转换
// c->cat_info(); /// 已经move 调用报错
cout << "ending......" << endl;
return 0;
不加const
void do_with_cat_pass_ref(std::unique_ptr<Cat> &c)
{
c->set_name("haha");
c->cat_info();
c.reset();
}
std::unique_ptr<Cat> u_p{new Cat("kiki")};
// 不加const
do_with_cat_pass_ref(u_p);
cout << "address:" << u_p.get() << endl; // 0x0
cout << "ending......" << endl;
return 0;
加const
void do_with_cat_pass_ref(const std::unique_ptr<Cat> &c)
{
c->set_name("haha"); // 居然还可以修改
c->cat_info();
// c.reset();
}
std::unique_ptr<Cat> u_p{new Cat("kiki")};
// 不加const
do_with_cat_pass_ref(u_p);
cout << "address:" << u_p.get() << endl;
cout << "ending......" << endl;
return 0;
std::unique_ptr<Cat> get_unique_ptr()
{
std::unique_ptr<Cat> u_p{new Cat("local cat")};
cout << u_p.get() << endl;
cout << &u_p << endl;
// get返回的是指针指向的内存地址
// &返回的是指针的地址
return u_p;
}
get_unique_ptr()->cat_info();
cout << "ending......" << endl;
return 0;
shared_ptr计数指针又称共享指针,与unique_ptr不同的是它是可以共享数据的。shared_ptr创建了一个计数器与泪对象所指的内存相关联,copy则计数器加一,销毁则计数器减一,查看计数的api为use_count().
常量类型
std::shared_ptr<int> s_p1 = make_shared<int>(10);
cout << "value:" << *s_p1 << endl; // 10
cout << "use count:" << s_p1.use_count() << endl; // 1
std::shared_ptr<int> s_p2 = s_p1;
cout << "s_p1 use count:" << s_p1.use_count() << endl; // 2
cout << "s_p2 use count:" << s_p2.use_count() << endl; // 2
*s_p2 = 30;
cout << "value:" << *s_p1 << endl; // 30
cout << "s_p1 use count:" << s_p1.use_count() << endl; // 2
cout << "s_p2 use count:" << s_p2.use_count() << endl; // 2
s_p2 = nullptr;
cout << "s_p1 use count:" << s_p1.use_count() << endl; // 1
cout << "s_p2 use count:" << s_p2.use_count() << endl; // 0
return 0;
自定义数据类型
std::shared_ptr<Cat> c_p1 = make_shared<Cat>();
cout << "c_p1 use_count:" << c_p1.use_count() << endl;
std::shared_ptr<Cat> c_p2 = c_p1;
std::shared_ptr<Cat> c_p3 = c_p2;
cout << "c_p1 use_count:" << c_p1.use_count() << endl;
cout << "c_p2 use_count:" << c_p2.use_count() << endl;
cout << "c_p3 use_count:" << c_p3.use_count() << endl;
c_p1.reset();
cout << "c_p1 use_count:" << c_p1.use_count() << endl;
cout << "c_p2 use_count:" << c_p2.use_count() << endl;
cout << "c_p3 use_count:" << c_p3.use_count() << endl;
return 0;
注意:不管计数是多少,数据在内存中只有一份,一次构造,一次销毁。
shared_ptr passed by value
copy
函数内部计数器加一
void cat_by_value(std::shared_ptr<Cat> cat)
{
cout << cat->get_name() << endl;
cat->set_name("haha");
cout << "func use count:" << cat.use_count() << endl; // 2
}
std::shared_ptr<Cat> c1 = make_shared<Cat>("kiki");
cat_by_value(c1);
c1->cat_info(); // haha
cout << "use count" << c1.use_count() << endl; //1
shared_ptr passed by ref
const表示不可改变指向
void cat_by_value(std::shared_ptr<Cat> cat)
{
cout << cat->get_name() << endl;
cat->set_name("haha");
cout << "func use count:" << cat.use_count() << endl; // 2
}
void cat_by_ref(std::shared_ptr<Cat> &cat)
{
cout << cat->get_name() << endl;
cat.reset(new Cat("dd")); // 加了const就不能调用
cout << "func use count:" << cat.use_count() << endl;
}
std::shared_ptr<Cat> c1 = make_shared<Cat>("kiki");
cat_by_value(c1);
c1->cat_info(); // haha
cout << "use count" << c1.use_count() << endl; //1
cat_by_ref(c1);
c1->cat_info();
return 0;
Construct of Cat: kiki
kiki
func use count:2
cat info name: haha
use count1
haha
Construct of Cat: dd
Destructor of Cat: haha
func use count:1
cat info name: dd
Destructor of Cat: dd
returning by value
链式调用
std::shared_ptr<Cat> get_shared_ptr()
{
std::shared_ptr<Cat> cat_ptr = make_shared<Cat>("local cat");
return cat_ptr;
}
不能将shared_ptr转换为unique_ptr,unique_ptr可以转换为shared_ptr(通过std::move)
常见的设计:将你的函数返回unique_ptr是一种常见的设计模式,这样可以提高代码的复用度,你可以随时改变为shared_ptr
std::unique_ptr<Cat> c_p1 = unique_ptr<Cat>{new Cat("kiki")};
std::shared_ptr<Cat> c_p2 = std::move(c_p1);
cout << c_p2.use_count() << endl; //1
std::shared_ptr<Cat> c_p3 = get_unique_ptr(); // 隐式转换
if(c_p3){
cout << c_p3.use_count() << endl; //1
}
weak_ptr并不拥有内存的所有权,并不能调用->和解引用*。
为什么需要weak_ptr呢?
假设A类中有一个需求需要存储其他A类对象的信息,如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(Cyclic dependency problem),所以这里需要一个不需要拥有所有权的指针来标记该同类对象(weak_ptr可以通过lock()函数来提升为shared_ptr(类型转换))
cat.h
#ifndef POINTER_CAT_H
#define POINTER_CAT_H
#include
#include
#include
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()
{
return name;
}
void set_name(const std::string &name)
{
this->name = name;
}
void set_friend(std::shared_ptr<Cat> c)
{
m_friend = c;
}
private:
std::string name;
std::shared_ptr<Cat> m_friend;
};
#endif //POINTER_CAT_H
std::shared_ptr<Cat> s_p1 = std::make_shared<Cat>("kiki");
std::weak_ptr<Cat> w_p1(s_p1);
cout << "s_p1 use count:" << s_p1.use_count() << endl; // 1
// weak_ptr可以调用use_count,但计数并不会加1
cout << "w_p1 use count:" << w_p1.use_count() << endl; // 1
// w_p1.cat_info() 报错
std::shared_ptr<Cat> s_p2 = w_p1.lock();
cout << "s_p1 use count:" << s_p1.use_count() << endl; // 2
cout << "s_p1 use count:" << s_p2.use_count() << endl; // 2
cout << "w_p1 use count:" << w_p1.use_count() << endl; // 2
cout << "##############循环依赖##############" << endl;
// 循环依赖
std::shared_ptr<Cat> cat1 = std::make_shared<Cat>("cat1");
std::shared_ptr<Cat> cat2 = std::make_shared<Cat>("cat2");
return 0;
Construct of Cat: kiki
s_p1 use count:1
w_p1 use count:1
s_p1 use count:2
s_p1 use count:2
w_p1 use count:2
##############循环依赖##############
Construct of Cat: cat1
Construct of Cat: cat2
Destructor of Cat: cat2
Destructor of Cat: cat1
Destructor of Cat: kiki
目前上述代码没有任何问题
std::shared_ptr<Cat> cat1 = std::make_shared<Cat>("cat1");
std::shared_ptr<Cat> cat2 = std::make_shared<Cat>("cat2");
// 将cat1的朋友设置为cat2
// 将cat2的朋友设置为cat1
cat1->set_friend(cat2);
cat2->set_friend(cat1);
Construct of Cat: kiki
s_p1 use count:1
w_p1 use count:1
s_p1 use count:2
s_p1 use count:2
w_p1 use count:2
##############循环依赖##############
Construct of Cat: cat1
Construct of Cat: cat2
Destructor of Cat: kiki
此时,cat1和cat2由于循环依赖并没有正常销毁,而程序也正常退出了。
解决这个问题,只需要将cat.h中的std::shared_ptr改为weak_ptr即可。
Construct of Cat: kiki
s_p1 use count:1
w_p1 use count:1
s_p1 use count:2
s_p1 use count:2
w_p1 use count:2
##############循环依赖##############
Construct of Cat: cat1
Construct of Cat: cat2
Destructor of Cat: cat2
Destructor of Cat: cat1
Destructor of Cat: kiki
修改后,都正常销毁了!