C++11之智能指针(unique_ptr、shared_ptr、weak_ptr、auto_ptr)浅谈内存管理

简介: C++11之智能指针(unique_ptr、shared_ptr、weak_ptr、auto_ptr)浅谈内存管理

前言

下面这段代码看起来正常,但事实在特殊情况下f函数可能无法释放这个a资源。

void f()
{
  A * a = new A();
  ...
  delete a;
}

例如:


1.如果在中间这段代码中有一个过早的return语句,且刚好被执行那么就会出现内存泄漏,这时你可能会说在每个return前都加一个delete a;语句。不得不说这种做法可以避免上述出现的问题,但会导致代码中随处可见的delete a;语句显得很乱且臃肿。

2.如果在中间这段代码中出现一个异常,也会导致内存泄漏。

单纯靠着delete a;语句去释放内存是行不通的。

智能指针

这时就要用到智能指针这个东西了。自动释放内存

  1. 智能指针会帮程序员来管理对象。
  2. 在智能智能释放时自动会调用所管理对象的析构函数。


头文件:memory

使用方法

虽然智能指针种类较多但是用法都一样,下面以unique_ptr为例。在申请到内存时将会交给unique_ptr接管,在执行完f函数后a对象将会自动调用析构函数。

void f()
{
    ...
  std::unique_ptr<A> a(new  A());
  ...
}


unique_ptr

对象的所有权可以从一个独占指针转移到另一个指针,

转移方式:对象始终只能有一个指针作为其所有者。

当独占指针离开其作用域或将要拥有不同的对象时,它会自动释放自己所管理的对象。


实现unique_ptr类

template<class T>
class Unique_ptr
{
public:
    Unique_ptr(T* ptr)
    {
        this->ptr = ptr;
    }
    ~Unique_ptr()
    {
        if (ptr != nullptr)
        {
            delete ptr;
            cout << "释放ptr对象" << endl;
            ptr = nullptr;
        }
    }
    T& operator*()
    {
        return *ptr;
    }
    T* get()
    {
        return ptr;
    }
    T* operator->()
    {
        return ptr;
    }
private:
    Unique_ptr(const Unique_ptr& other) = delete;
    Unique_ptr& operator=(const Unique_ptr& other) = delete;
private:
    T* ptr;
};


使用uniquePtr

int main()
{
    unique_ptr<string> p1(new string("hellow"));
    cout << *p1 << endl;
    *p1 = "你好!";
    cout << *p1 << endl;
    return 0;
}


shared_ptr

共享指针将记录有多少个指针共同享有某个对象的所有权。当有更多指针被设置为指向该对象时,引用计数随之增加;当指针和对象分离时,则引用计数也相应减少。当引用计数降低至0时,该对象被删除。


实现SharedPtr

template<class T>
class SharedPtr
{
public:
    SharedPtr(T* ptr = nullptr)
    {
        count = new int(1);
        this->ptr = ptr;
    }
    SharedPtr(SharedPtr& other)
    {
        *other.count += 1;
        count = other.count;
        ptr = other.ptr;
    }
    SharedPtr& operator=(SharedPtr& other)
    {
        *other.count += 1;
        count = other.count;
        ptr = other.ptr;
        return *this;
    }
    ~SharedPtr()
    {
        --(*count);
        cout << *count << "\t" << ptr << endl;
        if (ptr != nullptr && 0 == *count)
        {
            cout << "释放!" << endl;
            delete ptr;
            ptr = nullptr;
            delete count;
            count = nullptr;
        }
    }
    T& operator*() const
    {
        return *ptr;
    }
    T* get() const
    {
        return ptr;
    }
    T* operator->() const
    {
        return ptr;
    }
    int use_count(void) const
    {
        return *count;
    }
private:
    T* ptr;
    int* count;
};


使用shared_ptr

int main()
{
    Shared_ptr<int> p1(new int);
    Shared_ptr<int> p2(p1);
    *p1 = 10;
    cout << *p1 << endl;
    cout << *p2 << endl;
    cout << p1.use_count() << endl;//获取当前引用个数
    return 0;
}

weak_ptr

C++11标准虽然将 weak_ptr 定位为智能指针的一种,但该类型指针通常不单独使用(没有实际用处),只能和shared_ptr类型指针搭配使用。甚至可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具,借助weak_ptr类型指针, 我们可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。


使用weak_ptr

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }
    ~Test()
    {
        cout << "~Test()" << endl;
    }
    void Output() const
    {
        cout << "Output()" << endl;
    }
};
weak_ptr<Test> test()
{
    shared_ptr<Test> p(new Test);
    return p;
}
int main()
{
    weak_ptr<Test> w1 = test();
    shared_ptr<Test> s1 = w1.lock();//检查对象是否被释放
    cout << s1 << endl;
    if (s1)
    {
        cout << "对象依旧存在!" << endl;
    }
    else
    {
        cout << "对象已被释放" << endl;
    }
    return 0;
}

在Effective C++第13条款中也建议:如果想要手工释放资源,容易发送某些错误。使用这类智能指针往往能让程序员更少犯错。

目录
相关文章
|
19天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
45 4
|
2月前
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
2月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
44 1
|
5月前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
299 14
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
389 0
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
42 4
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
57 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配