智能指针相较于普通指针的区别,就是智能指针可以不用主动释放内存空间,系统会自动释放,避免了内存泄漏。
1、unique_ptr:独占指针
需包含的头文件:#include <memory>
unique_ptr 三种定义方式
先定义一个类
class cat
{
cat(std::string name);
~cat();
void cat_info() const //输出一下成员name的值
{
std::cout<<"name:"<<this->name<<std::endl;
}
void set_cat_name(const std::string &name) //修改成员name的值
{
this->name=name;
}
std::string get_name() const
{
return name;
}
private:
std::string name{"mimi"};
};
- 使用std::make_unique的方式:推荐使用的方式
//方法一 std::make_unique
std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
std::string a=u_c_p4->get_name();
std::cout << "a:" << a << std::endl;
- 使用new的方式声明智能指针
//方法二 new
std::unique_ptr<cat> u_c_p3{ new cat("cc")};
std::cout << "-------------" << std::endl;
我们只new了这个类对象,并没有delete释放他的地址,但是智能指针会在程序执行结束自动调用这个类的析构函数。
- 利用普通指针声明
cat *c_p2 = new cat("xy"); //声明一个普通指针
std::unique_ptr<cat> u_c_p2{ c_p2 }; // 声明一个智能指针指向普通指针的地址
//正能指针和普通指针的指向的地址相同,两个指针任意一个改变,对方也会跟着改变
c_p2->cat_info();
u_c_p2->cat_info();
c_p2->set_cat_name("aa");
c_p2->cat_info();
u_c_p2->cat_info();
u_c_p2->set_cat_name("bb");
c_p2->cat_info();
u_c_p2->cat_info();
std::cout << "-------------" << std::endl;
return 0;
用该方法声明智能指针有一个确定,普通指针依然可以使用,如果普通指针改变地址的值,那么智能指针的值也会跟着改变。
unique_ptr 做函数参数
- unique_ptr不能被复制,只可以move,即当要让unique_ptr 的变量做函数参数,直接将变量放在括号里是不可以的,可以用move函数
void do_unique_value(std::unique_ptr<cat> c)
{
c->cat_info();
}
int main()
{
//方法一 std::make_unique
std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
do_unique_value(u_c_p4); //直接赋值到括号里是不行的
u_c_p4->cat_info(); // 在move之前是可以调用的,但是move之后是不能调用的
do_unique_value(std::move(u_c_p4)); //要用move函数进行值传递
u_c_p4->cat_info(); //这里就能调用了
//但是在move之后这个变量是不能调用的
do_unique_value(std::make_unique<cat>()); // 直接构造
std::cout << "-------------" << std::endl;
return 0;
}
如果值是引用的话,可以不用move
void do_unique_value2(std::unique_ptr<cat> &c)
{
c->cat_info();
}
do_unique_value(u_c_p4); //参数是引用,就可以直接传值
u_c_p4->cat_info(); //并且之后也可以在使用该变量
2、shared_ptr 计数指针、共享指针
shared_ptr基础概念
shared_ptr
区别于unique_ptr
的区别是shared_ptr
可以被复制,shared_ptr
创建了一个计数器对象,与类对象所指向的内存地址关联。- copy一次
shared_ptr
的计数器就加1,销毁一次计数器就减一 - 调用
shared_ptr
计数器的API是use_count()
- 对
shared_ptr
指针变量的赋值后,use_count()
计数值就为1,然后将shared_ptr
变量赋给其他值,本身和另一个值的value值相同,计数值都为2,是在原本shared_ptr
值为1的基础上+1。 - 如果两个以上存储着相同地址的
shared_ptr
值,其中一个给赋值为nullptr,的话,那么只有被赋值为空的变量值为空,其余几个value值不变,但是计数值都会在原有基础上-1。
std::shared_ptr<int> i_p_1 = std::make_shared<int>(10);
std::cout << "value:" << *i_p_1 << std::endl;
// 第一次赋值计数为1
std::cout << "use count:" << i_p_1.use_count() << std::endl;
std::cout << "-------------" << std::endl;
//结果
// value:10
// use count : 1
//copy
std::shared_ptr<int> i_p_2 = i_p_1;
std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
//将i_p_1的值给i_p_2,对该地址有一次进行复制操作,计数+1,i_p_1和i_p_2代表的地址相同,存储值和计数值都相同
std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
std::cout << "-------------" << std::endl;
//结果:
//value:10
//value:10
//use count : 2
//use count : 2
//修改值
*i_p_2 = 30;
std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
std::cout << "-------------" << std::endl;
//结果
//i_p_1 value : 30
//i_p_2 value : 30
//i_p_1 use count : 2
//i_p_2 use count : 2
// 三个值表示同一个地址,将其中一个赋值为nullptr
std::shared_ptr<int> i_p_3 = i_p_1;
std::cout << "---------i_p_1 = nullptr befo---------" << std::endl;
std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
// 将i_p_1赋值给i_p_3,计数值在2的基础上+1=3
std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
i_p_1 = nullptr;
std::cout << "------i_p_1 = nullptr after-----------"<<std::endl;
//std::cout << "i_p_1 value:" << *i_p_1 << std::endl; //赋空之后不能输出
std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
//i_p_1赋值为空后,i_p_1的计数值直接为0,但是i_p_2和i_p_3的计数值会在原来3的基础上-1
std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
// 结果
// ---------i_p_1 = nullptr befo---------
//i_p_1 value : 30
//i_p_2 value : 30
//i_p_3 value : 30
//i_p_1 use count : 3
//i_p_2 use count : 3
//i_p_2 use count : 3
//------i_p_1 = nullptr after-----------
//i_p_2 value : 30
//i_p_3 value : 30
//i_p_1 use count : 0
//i_p_2 use count : 2
//i_p_2 use count : 2
shared_ptr函数
shared_ptr变量是可以copy,可以被当做值直接复制给函数,传入函数体后,在函数体内部计数值依然会进行加减操作,但操作尽在函数体内有效,出了函数体,计数值会恢复到原来的数值。但是在函数体里面对指针的值进行修改的话,该值会被修改。
void cat_by_value(std::shared_ptr<cat> cat)
{
cat->set_cat_name("ee");
cat->cat_info();
std::cout << "use count:" << cat.use_count() << std::endl;
}
std::shared_ptr<cat> c1 = std::make_shared<cat>("dd");
c1->cat_info();
std::cout << "c1 use count:" << c1.use_count() << std::endl;
std::cout << "-------------" << std::endl;
cat_by_value(c1);
std::cout << "-------------" << std::endl;
c1->cat_info();
std::cout << "c1 use count:" << c1.use_count() << std::endl;
//结果
//cat:dd
//name:dd
//c1 use count : 1
//------------ -
//name : ee
//use count : 2
//------------ -
//name : ee
//c1 use count : 1
//del : ee
3、shared_ptr与unique_ptr
- 不能将
shared_ptr
转化为unique_ptr
, - 可以将
unique_ptr
转化为shared_ptr
,使用std::move
如果函数有返回值的话,返回值最好设计为unique_ptr
,的类型,应为unique_ptr
既可以赋值给shared_ptr
,也可以赋值给shared_ptr
- 可以用move函数将
unique_ptr
的值赋给shared_ptr
类型 - 也可以将函数返回的
unique_ptr
类型的数据直接赋值给shared_ptr
类型
std::unique_ptr<cat> get_unique_ptr()
{
std::unique_ptr<cat> cat_p = std::make_unique<cat>("local cat");
return cat_p;
}
int main()
{
std::unique_ptr<cat> c_p_1 = std::make_unique<cat>("dd");
// 使用move的方式赋值
std::shared_ptr<cat> c_p_2 = std::move(c_p_1);
std::cout << "use count:" << c_p_2.use_count() << std::endl;
c_p_2->get_name();
std::cout << "use count:" << c_p_2.use_count() << std::endl;
std::cout << "-------------" << std::endl;
// 函数返回值赋值
std::shared_ptr<cat> c_p_3 = get_unique_ptr();
c_p_3->get_name();
std::cout << "use count:" << c_p_3.use_count() << std::endl;
std::cout << "-------------" << std::endl;
return 0;
}
weak_ptr
weak_ptr
没有内存的所有权,所以不能调用->
和解引用*
weak_ptr
可以通过lock()
提升为shared_ptr
类型
weak_ptr
不能单独存在,一般需要借助shared_ptr
,来声明
- 将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
- 使用lock(),将weak_ptr的值赋值给shared_ptr,两者的use_count()计数值都会相加。
std::shared_ptr<cat> s_p_1 = std::make_shared<cat>("c1");
//将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
std::weak_ptr<cat> w_p_1(s_p_1);
std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
std::cout << "-------------" << std::endl;
std::shared_ptr<cat> s_p_2 = w_p_1.lock();
std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
std::cout << "s_p_1 count:" << s_p_2.use_count() << std::endl;
//cat:c1 //构造函数
//s_p_1 count:1
//w_p_1 count:1
//-------------
//s_p_1 count:2
//w_p_1 count:2
//s_p_1 count:2
//del:c1 //析构函数
循环依赖的问题
class cat
{
public:
cat();
cat(std::string name);
~cat();
void set_frient(std::shared_ptr<cat> c)
{
m_friend = c;
}
private:
std::string name{ "mimi" };
std::shared_ptr<cat> m_friend;
};
std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
//运行结果
//cat:c3 //构造
//cat:c4 //构造
//del:c4 //析构
//del:c3 //析构
如果在c3和c4的类中都定义一个cat类型的变量存储着对方,那么两者就存在着依赖
std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
c3->set_frient(c4);
c4->set_frient(c3);
//运行结果,只够构造、没有析构
// cat:c3
// cat:c4
解决方式只需将类定义时的shared_ptr类型的对象改为weak_ptr
class cat
{
public:
cat();
cat(std::string name);
~cat();
void set_frient(std::shared_ptr<cat> c)
{
m_friend = c;
}
private:
std::string name{ "mimi" };
std::weak_ptr<cat> m_friend; //这需要把这里修改为weak_ptr类型
};
// 然后运行相同的代码
std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
c3->set_frient(c4);
c4->set_frient(c3);
//运行结果
//cat:c3
//cat:c4
//del:c4
//del:c3