首页 > 编程语言 >C++智能指针

C++智能指针

时间:2024-10-16 23:00:04浏览次数:6  
标签:cout 对象 C++ 智能 shared unique ptr 指针

智能指针

1、智能指针本身:智能指针是一个类模板的实例,通常作为局部变量存在于栈区(Stack)。当函数返回或者局部变量超出其作用域时,栈区的内存会被自动释放。

2、智能指针管理的对象:智能指针通常用来管理在堆区(Heap)上分配的内存。这是通过调用如new操作符来完成的。堆区的内存会一直存在,直到显式地释放它(使用delete操作符)或者当程序结束时才会被系统回收。

  1. C++对于普通的new或者malloc分配空间,需要及时的释放空间,否则容易造成内存泄漏,导致堆区空间不足。但是,随着代码量的增多,很容易忘记释放空间,于是智能指针应运而生。
  2. 智能指针会自动释放。省去手动管理内存。在智能指针中,会存在一个计数器,计数器的大小等于指向对象的智能指针的数量,当计数为0时,则释放指针。
  3. 智能指针在memory头文件中。
  • std::unique_ptr:它实现了对对象的独占所有权语义。这意味着一个unique_ptr在任何时候都指向一个对象,而且这个对象只能由一个unique_ptr拥有。当unique_ptr被销毁(例如离开其作用域)时,它所指向的对象也会被自动删除。
  • std::shared_ptr:它实现了对象的共享所有权语义。多个shared_ptr可以指向同一个对象,并且每个shared_ptr持有一个引用计数。当最后一个指向某个对象的shared_ptr被销毁或重置时,该对象才会被删除。
  • std::weak_ptr:它是对shared_ptr所管理对象的一个非拥有性引用,它指向一个由shared_ptr管理的对象,但并不影响对象的生命周期。也就是说,它不会增加所指向对象的引用计数。当最后一个指向对象的shared_ptr被销毁时,无论是否还有weak_ptr指向该对象,对象都会被删除。weak_ptr主要用于解决shared_ptr可能导致的循环引用问题。在这部分利用率局部变量的特性,C++的局部变量存在栈中,当变量的生命周期结束后,那栈会自动释放空间。而智能指针同样为局部变量,存在栈中。
//很简单的一个类,帮助解释智能指针
class Ball {
public:
	Ball() {
        cout << "creat Ball" << endl;
    }
    ~Ball() {
        cout << "delete Ball" << endl;
    }
    void bounce() {
        cout << "A Ball jumps" <<endl;
    }
};

1、智能指针类型

智能指针 类型
shared_ptr 共享指针,多个共享指针可以指向同一个对象
weak_ptr 该指针是对象的一个非拥有性引用,指向一个shared_ptr,主要用来避免shared_ptr环形依赖
unique_ptr 该指针独占对象的所有权,每个对象只能有一个该指针

2、shared_ptr

智能指针内部有一个计数器,当赋值给别的智能指针或者函数传参拷贝到另一个shared_ptr,计数器就会加1,当函数执行完毕,智能指针对象就被析构了,此时计数器就会减1,直到计数器变为0,说明没人在用这个对象了,就执行delete把它释放掉。

1、创建方式

//创建方式1
shared_ptr<int> p;
p = make_shared<int>(100);//此为推荐方式
//创建方式2
shared_ptr<int> p2{new int(100)};
//使用方式,和普通指针一样
shared_ptr<int> p3 = p;
cout << *p << endl;
*p3 = 321;
cout << *p3 << endl;

2、成员函数

1、use_count(),得到当前有多少个指针指向该对象

class Ball {};
shared_ptr<Ball> ball = make_shared<Ball>();
cout << ball.use_count << endl;//ball.use_count()展示当前有多少个指针指向这个物体,值为1
shared_ptr<Ball> ball2 = make_shared<Ball>();
cout << ball.use_count() << " " << ball2.use_count() << endl;//值为1 2
shared_ptr<Ball> ball3 = make_shared<Ball>();
cout << ball.use_count() << " " << ball2.use_count() << cout << " " << ball3.count() << endl;//值为1 2 3
ball.reset();//重置智能指针
ball2.reset();
ball3.reset();//重置智能指针,三个指针都释放后,ball会自动销毁,避免内存泄漏,也可以reset(new Ball)指向一个新的对象

2、get(),获得裸指针

Ball* rp = p.get();

rp指向当前资源,但若裸指针与共享指针都指向这块资源,若全部共享指针释放后,这块资源也会被释放,裸指针变成未定义的,所以尽可能避免裸指针和共享指针同时使用。

3、weak_ptr()

3.1环形依赖

循环引用是指两个或多个对象通过智能指针相互引用,导致它们的计数永远不为零,从而引发内存泄漏。

循环引用会在以下情况下产生:当两个或多个对象相互持有对方的智能指针(通常是shared_ptr)时,它们之间的引用计数将永远无法降到零,因为每个对象都在等待其他对象被销毁以释放自己。这种情况下,即使这些对象不再被程序的其他部分使用,它们所占用的内存也无法被释放,从而导致内存泄漏。通过使用weak_ptr代替其中一个或多个shared_ptr,可以打破这种循环引用,确保对象在不再需要时能够被正确销毁。

以下代码执行完后不会输出析构函数cout内容,说明这两个对象没有被删除,这是因为智能指针的计数没有变成0,但只要将Teacher类中的成员变量改为weak_ptr<School> school;即可避免环形依赖。

struct School;

struct Teacher {
    string name;
    int age;
    shared_ptr<School> school;//改为weak_ptr<School> school;即可避免环形依赖
    ~Teacher() {
        cout << "Teacher Destructed." << endl;
    }
};

struct School {
    string name;
    shared_ptr<Teacher> principle;
    
    ~School() {
        cout << "School Destructed." << endl;
    }
};

void test_range_conflict() {
    auto principle = make_shared<Teacher>();
    auto university = make_shared<School>();
    principle->school = university;
    university->principle = principle;
}

3.2weak_ptr

weak_ptr本身依赖shared_ptr存在,存储一个资源的引用但不能修改,只能告知资源是否存在。

cpp.lock(),返回shared_ptr地址指针,若对象已被释放,则返回nullptr

void test_weak_ptr() {
    weak_ptr<int> wp;
    shared_ptr<int> sp = make_shared<int>(100);
    wp = sp;
    auto resource = wp.lock();//wp.lock()返回指针,若被释放则返回nullptr
    if(resource){
        cout << "Number is" << *resource << endl;
    } else {
        cout << "wp is expired" endl;
    }
}

4、unique_ptr

  1. unique_ptr是独享的,不可以两个unique_ptr同时指向同一份资源
  2. 由于独占资源控制权,所以不支持普通拷贝,但可以转移控制权

4.1get()release()move()

创建unique_ptr指针

unique_ptr<Ball> ball = make_unique<Ball>();
Ball *p = ball.get();//获得裸指针
ball.reset(new Ball());//重新指向另一个Ball对象
Ball *ball2 = ball.release();//unique_ptr与资源解绑,release返回资源的裸指针,同时将unique_ptr设置为nullptr,但解绑后需要用delete释放
delete ball2;
ball = nullptr;//赋值为裸指针,也会释放所对应的资源

4.2转移控制权

unique_ptr<int> up1 = make_unique<int>(100);
unique_ptr<int> up2(up1.release());//方式1,转移控制权
unique_ptr<int> up3 = std::move(up1);//方式2,转移控制权

4.3在函数间传递unique_ptr

4.3.1只需要value

1.方式1

//1:只访问内容,引用,只访问资源
void pass_up1(int &value) {
    cout << value << endl;
}

void test_pass_up1() {
    auto up = make_unique<int>(123);
    pass_up1(*up);
}

2.方式2

//2:传递裸指针,避免unique_ptr的传递,同样只能访问unique_ptr指向的资源
void pass_up2(int *p) {
    cout << *p << endl;
}

void test_pass_up2() {
    auto up = make_unique<int>(123);
    pass_up2(up.get());
}
4.3.2传递unique_ptr

实质在做转移控制权

1.方式1

//3:改变unique_ptr本身,使用引用方式
void pass_up3(unique_ptr<int> &up) {
    cout << *up << endl;
    up.reset();
}

void test_pass_up3() {
    auto up = make_unique<int>(123);
    pass_up3(up);
    if (up == nullptr) cout << "up is upset" << endl;
}

2.方式2

//4:改变unique_ptr本身,使用move转移控制权
void pass_up4(unique_ptr<int> up) {
    cout << *up << endl;
    up.reset();
}

void test_pass_up4() {
    auto up = make_unique<int>(123);
    pass_up4(std::move(up));
    if (up == nullptr) cout << "up is upset" << endl;
}
4.4作为函数返回值

作为函数返回值,可以直接写return up;或者return(move(up));,前者不用写up是因为编译器智能识别返回类型,使用move constructor

//5:unique_ptr可以被作为函数返回值
unique_ptr<int> return_uptr(int value) {
    unique_ptr<int> up = make_unique<int>(value);
    rturn up;
}

void test_return_uptr() {
    unique_ptr<int> up = return_uptr(321);
    cout << *up <<endl;
}

标签:cout,对象,C++,智能,shared,unique,ptr,指针
From: https://www.cnblogs.com/milkchocolateicecream/p/18471119

相关文章

  • 电子商务行业的产品推荐系统变革:AI知识库引领智能化与个性化
    在数字化浪潮的席卷下,电子商务行业正以前所未有的速度发展,而产品推荐系统作为电商平台与用户之间的桥梁,其智能化与个性化水平的高低,直接关系到用户体验、转化率乃至整个平台的竞争力。近年来,随着人工智能(AI)技术的飞速发展,特别是AI知识库的构建与应用,电商行业的产品推荐系统正......
  • C++算法练习-day1——704.二分查找
    题目来源:.-力扣(LeetCode)题目思路分析二分查找是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从......
  • 【可答疑】基于51单片机的智能水温控制系统(含仿真、代码、报告、演示视频等)
     ✨哈喽大家好,这里是@每天一杯冰美式oh,985电子本硕,大厂嵌入式在职0.3年,业余时间做做单片机小项目,有需要也可以提供就业指导(免费)~......
  • C++ [NOIP1999 提高组] 邮票面值设计 详解
    C++[NOIP1999提高组]邮票面值设计详解题目背景题目描述输入格式输出格式样例#1样例输入#1样例输出#1完整代码(你们最想要的):[NOIP1999提高组]邮票面值设计题目背景除直接打表外,本题不保证存在正确且时间复杂度可以通过全部数据做法。由于测试数据过水,部......
  • C++基础语法---类和对象
    目录1、概念1.1对象:1.2类型:2、抽象3、封装4、对象的产生5、对象的大小6、 操作对象7、数据的保护和共享8、C++内置字符串操作类例:分文件形式---时钟类代码实现:总结:1、概念1.1对象:现实世界中一切客观存在的事物,统称为对象。对象是有形的,例如一杯水,一台......
  • 国家人工智能创新应用先导区数据及城市人工智能先导区准自然实验数据(2006-2023年)
    一、测算方式:参考C刊《当代财经》冯婉昕(2024)老师的做法,本文的核心解释变量为国家人工智能创新应用先导区政策(AI)。企业的金融资产配置是企业生产经营的内生变量,因此,如果选择企业层面的指标来度量企业人工智能应用情况,会面临较大的内生性问题,从而无法识别人工智能应用与金融资产......
  • 作物与杂草的智能识别,基于YOLOv8全系列参数模型【n/s/m/lx/】开发构建田间低头作物杂
    一、背景田间杂草的有效管理是现代农业生产中面临的重要挑战之一。杂草不仅竞争作物的养分、水分和阳光,还可能成为害虫和病原体的寄主,从而降低农作物的产量和品质。因此,开发高效、精确的杂草检测和管理系统对于提高农业生产效率、降低化学除草剂的使用以及保护环境具有重要......
  • 链表C++
    #include<iostream>#include<stdexcept>usingnamespacestd;#defineeleTypeintstructListNode{ eleTypem_data; ListNode*next; ListNode(eleTypedata) { m_data=data; next=NULL; }};classLinkedlist{private: ListNode*head; ......
  • C++:Boost的安装和使用
    1、Boost简介Boost的本质就是一个开源C++库,它包含多种功能强大的模块,如:字符串文本处理模块、容器、算法、多线程、智能指针、线程池等模块2、Boost的下载和安装(1)Boost下载官网:http://www.boost.org/SourceForge:C++BoostLibrary在国内能够实现更快速的下载window系......
  • 链队(c++)
    //队列的顺序实现//线性表先进先出#include<iostream>usingnamespacestd;#defineMaxSize100typedefstructLinkNode{chardata;structLinkNode*next;}LinkNode,*QueuePtr;typedefstruct{  QueuePtrfront,rear;}LinkQueue;//初始化voidInitQueue(L......