C++ --- C++智能指针
Posted Overboom
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ --- C++智能指针相关的知识,希望对你有一定的参考价值。
1. 为什么需要引入智能指针
智能指针是一个类,当超出了类的实例对象作用域时,会自动调用对象的析构函数来释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都是由程序员自己管理。但是使用普通指针容易造成堆内存泄露(忘记释放),二次释放等内存泄露问题,使用智能指针可以更好的管理堆内存。
2. C++11智能指针介绍
C++里面的四个智能指针包含在头文件中: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用。
2.1 std::shared_ptr的使用
1> 类似vector的作用,在使用智能指针时需要给它指定所指向的类型,指针的初始化可以通过构造函数、std::make_shared、reset()实现;
2> 对于一个未初始化的指针,调用reset()可以将其初始化,对于一个已经初始化的智能指针,调用reset()释放其所指对象;
3> 不能将指针直接赋值给一个智能指针,一个是指针,一个是类;
4> 调用get()函数获取原始指针
#include <iostream>
#include <memory>
int main()
{
std::shared_ptr<int> ptr1(new int(1)); //ptr1指向int类型
std::shared_ptr<int> ptr2(ptr1); //用ptr初始ptr2,此时ptr1指向的对象引用计数是2
std::cout << "ptr1 引用计数:" << ptr1.use_count() << std::endl;
std::cout << "ptr2 引用计数:" << ptr2.use_count() << std::endl;
int a = 20;
int *pint = &a;
pint = ptr1.get();
std::cout << *pint << std::endl;
std::shared_ptr<std::string> ptr3 = std::make_shared<std::string>("Hello World"); //ptr3指向string类型
std::cout << "ptr3 is : " << *ptr3 << std::endl;
std::cout << "ptr3 引用计数:" << ptr3.use_count() << std::endl;
std::shared_ptr<std::string> ptr4; //ptr4指向string类型
ptr4.reset(new std::string("reset init"));
std::cout << "ptr4 is : " << *ptr4<< std::endl;
std::cout << "ptr4 引用计数:" << ptr4.use_count() << std::endl;
ptr4 = ptr3;
std::cout << "ptr3 引用计数:" << ptr3.use_count() << std::endl;
std::cout << "ptr4 引用计数:" << ptr4.use_count() << std::endl;
ptr4.reset();
std::cout << "ptr3 引用计数:" << ptr3.use_count() << std::endl;
std::cout << "ptr4 引用计数:" << ptr4.use_count() << std::endl;
//std::shared_ptr<int> ptr5 = new int(1); //error
return 0;
}
编译输出:
5> 避免循环引用
#include <iostream>
#include <memory>
class B;
class A {
public:
~A()
{
std::cout << "A is des" << std::endl;
}
std::shared_ptr<B> aptr;
};
class B {
public:
~B()
{
std::cout << "B is des" << std::endl;
}
std::shared_ptr<A> bptr;
};
void test()
{
std::shared_ptr<A> ptr1(new A());
std::shared_ptr<B> ptr2(new B());
// ptr1->aptr = ptr2; //循环引用
// ptr2->bptr = ptr1;
std::cout << "A use count is " << ptr1.use_count() << std::endl;
std::cout << "B use count is " << ptr2.use_count() << std::endl;
}
int main()
{
test();
return 0;
}
如果将test()函数中的两行注释打开,就是造成循坏引用,类A与类B的西沟函数不会被调用,A和B的引用计数都是2,离开作用于变成1,永远不会被析构, 出现内存泄露。
2.2 std::unique_ptr的使用
1> unique_ptr是独占型的指针,某个时刻只能有一个std::unique_ptr指向一个给定对象。当std::unique_ptr被销毁的时候,它所指向的对象也会被销毁,不像std::shared_ptr共享所指向的对象;
2> 在unique_ptr的声明周期内,可以改变智能指针的指向对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
#include <iostream>
#include <memory>
int main() {
{
std::unique_ptr<int> uptr(new int(10)); //绑定动态对象
//std::unique_ptr<int> uptr2 = uptr; //不能賦值
//std::unique_ptr<int> uptr2(uptr); //不能拷貝
std::unique_ptr<int> uptr2 = std::move(uptr); //转换所有权
uptr2.release(); //释放所有权
}
//超過uptr的作用域,內存釋放
}
2.3 std::weak_ptr的使用
weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。
#include <iostream>
#include <memory>
int main() {
{
std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
std::cout << sh_ptr.use_count() << std::endl;
std::weak_ptr<int> wp(sh_ptr);
std::cout << wp.use_count() << std::endl;
if(!wp.expired()){
std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr
*sh_ptr = 100;
std::cout << wp.use_count() << std::endl;
}
}
//delete memory
}
2.4 循环引用
考虑一个简单的对象建模——家长与子女:a Parent has a Child, a Child know his/her Parent。如果使用原始指针作为成员,Child和Parent由谁释放?那么如何保证指针的有效性?如何防止出现空悬指针?这些问题是C++面向对象编程麻烦的问题,现在可以借助smart pointer把对象语义(pointer)转变为值(value)语义,shared_ptr轻松解决生命周期的问题,不必担心空悬指针。但是这个模型存在循环引用的问题,注意其中一个指针应该为weak_ptr。
原始指针的做法,容易出错
#include <iostream>
#include <memory>
class Child;
class Parent;
class Parent {
private:
Child* myChild;
public:
void setChild(Child* ch) {
this->myChild = ch;
}
void doSomething() {
if (this->myChild) {
}
}
~Parent() {
delete myChild;
}
};
class Child {
private:
Parent* myParent;
public:
void setPartent(Parent* p) {
this->myParent = p;
}
void doSomething() {
if (this->myParent) {
}
}
~Child() {
delete myParent;
}
};
int main() {
{
Parent* p = new Parent;
Child* c = new Child;
p->setChild(c);
c->setPartent(p);
delete c; //only delete one
}
return 0;
}
循坏引用内存泄露的问题
#include <iostream>
#include <memory>
class Child;
class Parent;
class Parent {
private:
std::shared_ptr<Child> ChildPtr;
public:
void setChild(std::shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
if (this->ChildPtr.use_count()) {
}
}
~Parent() {
}
};
class Child {
private:
std::shared_ptr<Parent> ParentPtr;
public:
void setPartent(std::shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
std::weak_ptr<Parent> wpp;
std::weak_ptr<Child> wpc;
{
std::shared_ptr<Parent> p(new Parent);
std::shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
std::cout << p.use_count() << std::endl; // 2
std::cout << c.use_count() << std::endl; // 2
}
std::cout << wpp.use_count() << std::endl; // 1
std::cout << wpc.use_count() << std::endl; // 1
return 0;
}
正确的做法
#include <iostream>
#include <memory>
class Child;
class Parent;
class Parent {
private:
//std::shared_ptr<Child> ChildPtr;
std::weak_ptr<Child> ChildPtr;
public:
void setChild(std::shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
//new shared_ptr
if (this->ChildPtr.lock()) {
}
}
~Parent() {
}
};
class Child {
private:
std::shared_ptr<Parent> ParentPtr;
public:
void setPartent(std::shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {
}
}
~Child() {
}
};
int main() {
std::weak_ptr<Parent> wpp;
std::weak_ptr<Child> wpc;
{
std::shared_ptr<Parent> p(new Parent);
std::shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
std::cout << p.use_count() << std::endl; // 2
std::cout << c.use_count() << std::endl; // 1
}
std::cout << wpp.use_count() << std::endl; // 0
std::cout << wpc.use_count() << std::endl; // 0
return 0;
}
参考链接:
https://www.cnblogs.com/wxquare/p/4759020.html
https://zhuanlan.zhihu.com/p/157677773
https://www.cnblogs.com/WindSun/p/11444429.html
以上是关于C++ --- C++智能指针的主要内容,如果未能解决你的问题,请参考以下文章