首页 > 其他分享 >智能指针

智能指针

时间:2023-02-20 20:01:37浏览次数:39  
标签:auto 智能 Test new ptr 指针

原文链接:C++ 智能指针

一、使用智能指针原因

智能指针就是帮C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏

如下例子就是内存泄露的例子:

#include <iostream>
#include <string>
#include <memory>

using namespace std;


// 动态分配内存,没有释放就return
void memoryLeak1() {
    string *str = new string("动态分配内存!");
    return;
}

// 动态分配内存,虽然有些释放内存的代码,但是被半路截胡return了
int memoryLeak2() {
    string *str = new string("内存泄露!");

    // ...此处省略一万行代码

    // 发生某些异常,需要结束函数
    if (1) {
        return -1;
    }
    /
    // 另外,使用try、catch结束函数,也会造成内存泄漏!
    /

    delete str;    // 虽然写了释放内存的代码,但是遭到函数中段返回,使得指针没有得到释放
    return 1;
}


int main(void) {

    memoryLeak1();

    memoryLeak2();

    return 0;
} 

memoryLeak1函数中,new了一个字符串指针,但是没有delete就已经return结束函数了,导致内存没有被释放,内存泄露!

memoryLeak2函数中,new了一个字符串指针,虽然在函数末尾有些释放内存的代码delete str,但是在delete之前就已经return了,所以内存也没有被释放,内存泄露!

使用指针,我们没有释放,就会造成内存泄露。但是我们使用普通对象却不会!

思考:如果我们分配的动态内存都交由有生命周期的对象来处理,那么在对象过期时,让它的析构函数删除指向的内存,这看似是一个 very nice 的方案?

智能指针就是通过这个原理来解决指针自动释放的问题!

  1. C++98 提供了 auto_ptr 模板的解决方案
  2. C++11 增加unique_ptr、shared_ptr 和weak_ptr

二、auto_ptr(废弃)

auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存!

(一)用法

头文件: #include < memory >

用 法: auto_ptr<类型>  变量名(new 类型)

例如:

auto_ptr<string>  str(new string(“我要成为大牛~ 变得很牛逼!”));
auto_ptr<vector<int>>  av(new vector< int >());
auto_ptr<int>  array(new int[10]);

(二)例子

我们先定义一个类,类的构造函数和析构函数都输出一个字符串用作提示!
定义一个私有成员变量,赋值20.
再定义一个私有成员方法用于返回这个私有成员变量。

class Test {
public:
    Test() { cout << "Test的构造函数..." << endl; }
    ~Test() { cout << "Test的析构函数..." << endl; }

    int getDebug() { return this->debug; }

private:
    int debug = 20;
};

当我们直接new这个类的对象,却没有释放时:

int main(void) {
    Test *test = new Test;

    return 0;
} 

输出:

可以看到,只是打印了构造函数这个字符串,而析构函数的字符却没有被打印,说明并没有调用析构函数!这就导致了内存泄露!

解决内存泄露的办法,要么手动delete,要么使用智能指针!

使用智能指针:

// 定义智能指针
auto_ptr<Test> test(new Test);

智能指针可以像普通指针那样使用:

cout << "test->debug:" << test->getDebug() << endl;
cout << "(*test).debug:" << (*test).getDebug() << endl;

这时再试试:

int main(void) {

    //Test *test = new Test;
    auto_ptr<Test> test(new Test);

    cout << "test->debug:" << test->getDebug() << endl;
    cout << "(*test).debug:" << (*test).getDebug() << endl;

    return 0;
} 

使用智能指针后,输出为:

自动调用了析构函数。

(三)为什么智能指针可以像普通指针那样使用

因为其里面重载了 * 和 -> 运算符, * 返回普通对象,而 -> 返回指针对象。

(具体原因不用深究,只需知道他为什么可以这样操作就行!)

函数中返回的是调用get()方法返回的值

(四)智能指针的三个常用函数

1.get() 获取智能指针托管的指针地址

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp = test.get();        // 获取指针返回
cout << "tmp->debug:" << tmp->getDebug() << endl;

但我们一般不会这样使用,因为都可以直接使用智能指针去操作,除非有一些特殊情况。

get()函数原型:

_NODISCARD _Ty * get() const noexcept
{    // return wrapped pointer
    return (_Myptr);
}

2.release() 取消智能指针对动态内存的托管

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp2 = test.release();    // 取消智能指针对动态内存的托管
delete tmp2;    // 之前分配的内存需要自己手动释放

也就是智能指针不再对该指针进行管理,改由管理员进行管理!

release()函数原型:

_Ty * release() noexcept
{    // return wrapped pointer and give up ownership
    _Ty * _Tmp = _Myptr;
    _Myptr = nullptr;
    return (_Tmp);
}

3.reset() 重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉

// 定义智能指针
auto_ptr<Test> test(new Test);

test.reset();            // 释放掉智能指针托管的指针内存,并将其置NULL

test.reset(new Test());    // 释放掉智能指针托管的指针内存,并将参数指针取代之

reset函数会将参数的指针(不指定则为NULL),与托管的指针比较。如果地址不一致,那么就会析构掉原来托管的指针,然后使用参数的指针替代之,然后智能指针就会托管参数的那个指针了。

reset()函数原型:

void reset(_Ty * _Ptr = nullptr)
{    // destroy designated object and store new pointer
    if (_Ptr != _Myptr)
        delete _Myptr;
    _Myptr = _Ptr;
}

(五)使用建议

1.尽可能不要将auto_ptr 变量定义为全局变量或指针;

// 没有意义,全局变量也是一样
auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);    

当设定为全局变量的时候,比如全局定义这样的一个对象:auto_ptr t(new Test()),那t这个对象只有在程序执行结束的时候才会被释放,就达不到我们使用它的目的和初衷。使用智能指针就没有意义了

2.除非自己知道后果,不要把auto_ptr 智能指针赋值给同类型的另外一个 智能指针;

auto_ptr<Test> t1(new Test);
auto_ptr<Test> t2(new Test);
t1 = t2;    // 不要这样操作...

可以发现cout<<ap2->m_data<<endl;未输出

当一个auto_ptr的智能指针对象拷贝其他指针的值后, 之前的的auto_ptr就失效了。这里的拷贝指的是拷贝构造和对象之间的赋值(即赋值运算符重载) 。auto_ptr为什么要这样做呢? 事出反常必有妖, 原因就是堆区内存不能重复释放, 但当多个auto_ptr智能指针都指向同一片堆区内存时, 每一个auto_ptr智能指针最终都会释放, 这就会导致重复释放的问题。所以为了避免这种bug产生, auto_ptr索性采取一种托管的思想, 指针只有一份, 给你我就没有了, 即在拷贝之后, 直接让旧的失效这样就避免的重复释放的问题. 但也导致了之前的智能指针不能用

3.C++11 后auto_ptr 已经被“抛弃”,已使用unique_ptr替代!C++11后不建议使用auto_ptr。

4.auto_ptr 被C++11抛弃的主要原因

(1) 复制或者赋值都会改变资源的所有权

// auto_ptr 被C++11抛弃的主要原因
auto_ptr<string> p1(new string("I'm Li Ming!"));
auto_ptr<string> p2(new string("I'm age 22."));

cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

// p2赋值给p1后,首先p1会先将自己原先托管的指针释放掉,然后接收托管p2所托管的指针,
// 然后p2所托管的指针制NULL,也就是p1托管了p2托管的指针,而p2放弃了托管。
p1 = p2;    
cout << "p1 = p2 赋值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

(2)在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值

vector<auto_ptr<string>> vec;
auto_ptr<string> p3(new string("I'm P3"));
auto_ptr<string> p4(new string("I'm P4"));

// 必须使用std::move修饰成右值,才可以进行插入容器中
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));

cout << "vec.at(0):" <<  *vec.at(0) << endl;
cout << "vec[1]:" <<  *vec[1] << endl;


// 风险来了:
vec[0] = vec[1];    // 如果进行赋值,问题又回到了上面一个问题中。
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

访问越界了!

(3)不支持对象数组的内存管理

auto_ptr<int[]> array(new int[5]);    // 不能这样定义

所以,C++11用更严谨的unique_ptr 取代了auto_ptr!

(六)总结

测试代码:

#include <iostream>
#include <string>
#include <memory>
#include <vector>

using namespace std;

class Test {
public:
    Test() { cout << "Test的构造函数..." << endl; }
    ~Test() { cout << "Test的析构函数..." << endl; }

    int getDebug() { return this->debug; }

private:
    int debug = 20;
};

// 不要定义为全局变量,没有意义
//auto_ptr<Test> test(new Test);

void memoryLeak1() {
    //Test *test = new Test;

    // 定义智能指针
    auto_ptr<Test> test(new Test);
    
    cout << "test->debug:" << test->getDebug() << endl;
    cout << "(*test).debug:" << (*test).getDebug() << endl;


    // get方法
    Test *tmp = test.get();        // 获取指针返回
    cout << "tmp->debug:" << tmp->getDebug() << endl;


    // release方法
    Test *tmp2 = test.release();    // 取消智能指针对动态内存的托管
    delete tmp2;    // 之前分配的内存需要自己手动释放


    // reset方法:重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉
    test.reset();            // 释放掉智能指针托管的指针内存,并将其置NULL
    test.reset(new Test());    // 释放掉智能指针托管的指针内存,并将参数指针取代之


    // 忠告:不要将智能指针定义为指针
    //auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);

    // 忠告:不要定义指向智能指针对象的指针变量
    //auto_ptr<Test> t1(new Test);
    //auto_ptr<Test> t2(new Test);
    //t1 = t2;

    return;
}

int memoryLeak2() {
    //Test *test = new Test();

    // 定义智能指针
    auto_ptr<Test> test(new Test);

    // ...此处省略一万行代码

    // 发生某些异常,需要结束函数
    if (1) {
        return -1;
    }

    //delete test;
    return 1;
}


int main1(void) {

    //memoryLeak1();

    //memoryLeak2();

    //Test *test = new Test;
    //auto_ptr<Test> test(new Test);

    //cout << "test->debug:" << test->getDebug() << endl;
    //cout << "(*test).debug:" << (*test).getDebug() << endl;


     auto_ptr 被C++11抛弃的主要原因
    //auto_ptr<string> p1(new string("I'm Li Ming!"));
    //auto_ptr<string> p2(new string("I'm age 22."));
    //
    //cout << "p1:" << p1.get() << endl;
    //cout << "p2:" << p2.get() << endl;

    //p1 = p2;
    //cout << "p1 = p2 赋值后:" << endl;
    //cout << "p1:" << p1.get() << endl;
    //cout << "p2:" << p2.get() << endl;



    // 弊端2.在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制
    vector<auto_ptr<string>> vec;
    auto_ptr<string> p3(new string("I'm P3"));
    auto_ptr<string> p4(new string("I'm P4"));

    vec.push_back(std::move(p3));
    vec.push_back(std::move(p4));

    cout << "vec.at(0):" <<  *vec.at(0) << endl;
    cout << "vec[1]:" <<  *vec[1] << endl;


    // 风险来了:
    vec[0] = vec[1];
    cout << "vec.at(0):" << *vec.at(0) << endl;
    cout << "vec[1]:" << *vec[1] << endl;


    // 弊端3.不支持对象数组的内存管理
    //auto_ptr<int[]> array(new int[5]);    // 不能这样定义
    return 0;
} 

三、unique_ptr

 

标签:auto,智能,Test,new,ptr,指针
From: https://www.cnblogs.com/imreW/p/17138730.html

相关文章

  • 智能通信网关管理设备(借助工业智能网关实现工业设备在线监控和维护)
    方案背景工厂的设备都需要定时检修维护,保障稳定运行和安全生产。无论是日常检查还是故障维护,都需要花费相当多的人力、物力和资金,也需要面对停工维护带来的损失。在传统维护......
  • 垃圾回收站视频智能分析系统助力垃圾分类共享美好生活
    ​近年来各大城市陆续市推行垃圾分类,在街道、社区和单位设立智能垃圾分类回收站。目前很多社区也已落地智能回收站,多个场所正在接入并陆续投入使用。对比社区四分类垃圾桶的......
  • 公交车载智能视频监控:公交运行的网络安全墙​
    ​随着我国城市化的发展,城市公交作为城市交通运输的重要组成部分,成为大家低碳环保、绿色出行的首选交通工具。由于城市公交属于公共交通,与出行乘客、司机的公共财产、人身安......
  • 常量指针和指针常量
    指针常量:本质是指针,但是指向常量,也就是可以改变指向,但是不可以改变指向的值-常量指针:本质是指针,但是是常量的指针,不可以改变指向,但是可以改变指向的值a和&a有什么区别?......
  • 指针进阶2 - 函数
    1.函数指针函数名VS&函数名对于数组而言,数组名=首元素地址,&数组名=整个数组的地址那么函数名和&函数名等于什么#include<stdio.h>voidtest(){ ;}intmain()......
  • 如何基于安防手段进行高速公路服务区的智能化管理?​
    ​高速公路服务区作为高速公路的重要组成部分,能够为出行人员提供加油、如厕、餐饮、休息等刚需服务,一直以来被认为是高速公路重要的辅助产业。而服务区的信息化建设也在经营......
  • 指针和引用的区别
    指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名指针可以有多级,引用只有一级指针可以为空,引用不能为NULL且在定义时必须初始化指针......
  • 【C指针进阶】(C精髓)——对指针的更进一步深入剖析(图文近2w详解)
    @​​TOC​​前言我们都知道,指针是C语言中必不可少的一部分,是C语言的精髓所在,一个学习C语言的人如果不对指针有着深刻的理解,那还不算得上是真正入门,本篇文章整理了对于指针......
  • Unreal 各种指针类型是怎么回事
    引言读完本篇文章,你会了解为何UE中C++作为其开发语言,使用的指针,为何各式各样。你需要对UE有所了解,如果不了解也没关系,也可以看下这篇文章,就当了解一下最复杂的应用的系统......
  • 指针和字符串基础知识
    #include"stdafx.h"intmain(intargc,char*argv[]){//定义字符串的第一种方式,此种定义的字符可修改chara[]="it";a[0]='T';printf("%s\n",a);//定义字......