c++—内存管理、智能指针、内存池

文章介绍了内存管理的不同方式,包括用户管理和系统管理,以及内存分配的静态和动态方法。提到了防止内存泄漏的策略,如使用valgrind工具和智能指针。C++中没有内置的垃圾回收机制,因为这不符合其设计哲学,但提供了unique_ptr、shared_ptr和weak_ptr等智能指针来自动管理内存。此外,文章还讨论了内存池的概念,作为优化内存分配的一种手段。
摘要由CSDN通过智能技术生成

1. 内存分析诊断工具:valgrind;

2. 内存管理的两种方式:

        ①用户管理:自己申请的,自己用,自己回收;效率高,但容易导致内存泄漏;

        ②系统管理:系统自动回收垃圾;安全性高,但消耗内存资源;

3. 内存分配的两种方式:

(1)静态分配(数组),存在于栈空间;

        ①优点:物理地址连续,方便遍历;

        ②缺点:分配与利用效率较低;

(2)动态分配(malloc、new),存在于堆空间;

        ①优点:使用效率较高;

        ②缺点:物理地址不连续;

4. 防止内存泄漏的方法:

        ①养成良好的编码规范;

        ②使用指针时,初始化就置空,避免野指针;

        ③malloc后及时free;new后及时delete;

        ④利用内存检测工具,如valgrind进行辅助处理;且可以利用g++选项工具同步分析;

5. 为什么c++没有GC机制?

        ①没有共同的基类:c++从C语言演变而来,可以直接操作指针,且类型之间可以相互转换,对于一个指针无法直到它实际指向类型;

        ②系统开销比较大:垃圾回收带来的系统开销,需要占用更多的内存,违反了c++的设计哲学“不为不必要的功能支付代价”,不符合高效特性;

        ③且c++中有析构函数实现智能指针,通过引用计数来管理资源的释放,对GC的需求不迫切;

6. 智能指针

    (1)智能指针的作用:帮助开发者对动态分配的对象进行声明周期管理,可以有效地防止内存泄漏;

    (2)分类,可以分为三类

        ①unique_ptr:独占指针

        ②shared_ptr:共享指针

        ③weak_ptr:弱指针

    (3)shared_ptr:共享指针

        ①共享:多个指针可以同时指向同一个对象,当最后一个指针被销毁或者指向其他对象时,这个对象会被释放;

        ②工作原理:内部有计数器,实时记录该共同空间被几个智能指针指向;其本质是对裸指针进行分装,下有封装例子;

        ③导致引用计数增加的情况:用一个智能指针初始化另一个智能指针、函数传参(传递一个智能指针,即复制),函数返回值(返回一个智能指针);

        ④引用计数减少的情况:给智能指针赋予新值指向一个新的对象、局部的智能指针离开作用域;

        ⑤语法:(注意:初始化不可以用 “ = ” 号),有两种初始化的方法(手动初始化、函数初始化std::make_shared函数)

shared_ptr<int>pi(new int(5));  //(初始化1)内部调用的构造函数
shared_ptr<int>pi = new int(5); //错误用法!
auto p = std::make_shared<string>("hello world");  //(初始化2)函数方法
int *p = pi.get();  //获得pi的裸指针,注意智能指针一旦释放,裸指针也就失效;
pi.reset();  //将pi置空;
pi.use_count();  //返回该智能指针的引用计数;
pi.reset(new int (6)); //改变pi的指向;
delete p;  //释放;
delete p;  //释放数组指针;

void test(shared_ptr<int>&p)  //智能指针作为形参
{

}

reinterpret_pointer_cast<>();  //可以将任意类型的指针进行转换;

        ⑥自定义删除器,一些情况下,默认删除器处理不了(shared_ptr管理动态数组),需要自己指定删除器,下有详细例子;

        ⑧指针类型转换函数

static_pointer_cast  //void*与裸指针转换
dynamic_pointer_cast //向下类型转型(基类→派生类),基类需要有虚函数;
const_pointer_cast   //去除裸指针的const属性;
reinterpret_pointer_cast //任意类型之间裸指针转换;

    (4)unique_ptr:独占指针

        ①同一时刻,只能有一个unique_ptr指向这个对象,当指针销毁,指向的对象也销毁;

        ②语法,(注意:初始化不可以用 “ = ” 号),有两种初始化的方法(手动初始化、函数初始化std::make_unique函数)

unique_ptr<int>p(new int(5));
unique_ptr<int>p = new int(5);  //错误用法:不可以使用=
atuo pi = std::make_unique<string>("hello world");

        ③其他操作方法例如reset()、get()等于shared_ptr一样;

    (5)weak_ptr:独占指针

        ①weak_ptr是弱指针,不是独立的指针,不能单独操作所指向的资源;

        ②用处一:weak_ptr辅助shared_ptr的使用(监视shared_ptr 指向对象的生命周期);

        ③用处二:weak_ptr和shared_ptr之间可以相互转换,shared_ptr可以赋给weak_ptr,反过来不可以;

      (6)自己封装的智能指针shared_ptr<>();

hpp:

#pragma once
#include <iostream>
using namespace std;
//自己封装的智能指针shared_ptr<>();
template <typename T>
class Smartpointer
{
public:
    template <typename U>
    friend class Smartpointer;  //声明一种友元类

    Smartpointer() : m_p(nullptr)
    {
        //m_p = nullptr;
        m_count = new long(0);
    }

    explicit Smartpointer(T * p)  //禁止隐式转换,就是不可以使用=,只能使用()初始化
    {
        cout<<"explicit"<<endl;
        if(p != nullptr)
        {
            m_p = p;
            m_count = new long(1);  //次数计为1
        }
        else
        {
            m_p = nullptr;
            m_count = new long(0);  //次数为0
        }
    }

    template <typename U>
    Smartpointer(T *p, Smartpointer<U> &other)  //另外一种类型U?
    {
        if(p != nullptr)
        {
            m_p = other.m_p;
            m_count = other.m_count;
            (*m_count)++;
        }
        else
        {
            m_p = nullptr;
            m_count = other.m_count;
        }
    }

    Smartpointer(Smartpointer<T> &other)  //拷贝构造
    {
        cout<<"拷贝构造"<<endl;
        if(other.m_p != nullptr)
        {
            m_p = other.m_p;
            m_count = other.m_count;
            (*m_count)++;
        }
        else
        {
            m_p = nullptr;
            m_count = other.m_count;
        }
    }

    Smartpointer<T> & operator=(const Smartpointer<T> &other)  //=重载
    {
        cout<<"operator="<<endl;
        if(other.m_p != nullptr)
        {
            m_p = other.m_p;
            m_count = other.m_count;
            (*m_count)++;
        }
        else
        {
            m_p = other.m_p;
            m_count = other.m_count;
        }
        return *this;
    }

    Smartpointer(Smartpointer<T>&&other)  //移动构造
    {
        cout<<"移动构造"<<endl;
        if(other.m_p = nullptr)
        {
            m_p = other.m_p;
            other.m_p = nullptr;
            m_count = other.m_count;
            other.m_count = nullptr;
        }
        else
        {
            m_p = nullptr;
            m_count = other.m_count;
        }
    }

    ~Smartpointer()  //析构函数
    {
        if((m_p != nullptr) && (--(*m_count) == 0))
        {
            cout<<"~Smartpointer"<<endl;
            delete m_p;
            delete m_count;
        }
        else
        {
            m_p = nullptr;
            m_count = nullptr;
        }
    }

    long use_count() const  //函数后面加const的作用?
    {
        return *m_count;
    }

    T * get()  //返回裸指针
    {
        return m_p;
    }

    void reset(T *p)
    {
        if(p = nullptr)
        {
            m_p = p;
            (*m_count)--;
            m_count = new long(1);  //引用计数从新置为1
        }
        else
        {
            m_p = nullptr;
            m_count = new long(0);
        }
    }

    void reset()
    {
        m_p = nullptr;
        (*m_count)--;
    }

    void swap(Smartpointer<T> &other)
    {
        std::swap(m_p,other.m_p);  //c库内置函数实现交换
        std::swap(m_count,other.m_count);
    }

    T operator*()
    { 
        cout<<"operator*"<<endl;
        return *m_p;
    }

    T * operator->()
    {
        return m_p;
    }

    operator bool()
    {
        return m_p != nullptr;
    }

    T operator[](int index)  //重载[]
    {
        return m_p[index];
    }

    template <typename T1, typename U1>
    friend Smartpointer<T1> static_pointer_cast(Smartpointer<U1>&other);  //声明一种友元函数,用于不同类型的智能指针转换

private:
    T * m_p;  //裸指针
    long * m_count;  //引用计数指针
};

template <typename T, typename U>
Smartpointer<T> static_pointer_cast(Smartpointer<U>&other)
{
    T * temp = static_cast<T *>(other.m_p);
    return Smartpointer(temp,other);
}

cpp:

#include "smart_pointer.hpp"
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char **argv)
{
    Smartpointer<int>t1(new int (5));
    cout<<(*t1)<<endl;
    cout<<t1.use_count()<<endl;

    Smartpointer<int>t2(t1);
    cout<<t2.use_count()<<endl;
    cout<<t1.use_count()<<endl;

    Smartpointer<string>t3(new string ("hello world"));
    // Smartpointer<string>t2(t4,t3);
    //Smartpointer<int> static_pointer_cast(t3);  //指针类型转换没有成功?

    Smartpointer<int>t4 = t2;
    cout<<(*t4)<<endl;
    cout<<t4.use_count()<<endl;

    Smartpointer<int>t5(std::move(t4));

    return 0;
}

7. 内存池

    (1)new内存分配细节

        ①new分配内存实际是调用malloc函数进行内存分配的;

        ②malloc实际分配内存时,不单单分配需要的内存大小,还要附加大量的附带内存,用以记录相关使用信息,包括记录分配了多少个字节(占4字节)、调试信息(占30-60字节)、边界调整(占10字节)、尾信息(4字节),也就是说除了实际分配的内存(比如给一个int变量分配4字节),还要附加70字节左右的附加内存,内存浪费很严重;尤其是频繁申请小块内存时,浪费更加严重;

        ③重载new操作符、重载delete操作符

void *operator new(size_t size)
{
    int *p = (int *)malloc(sizeof(int)):
    return *p;    
}

void *operator new[](size_t size)
{
    void *p = malloc(size);  //内部会有转换机制,将你输入的个数,乘以类型的单位字节,算出来总数字节赋给size;
    return p;
}

void *operator delete[](void *p)
{
    free(p);
}

void *operator delete(void *p)
{
    free(p);
}

        ④定位new(pleacement new)

        在已经分配的原始内存中初始化一个对象,相当于从之前已经分配好的大块内存中取出一块来给新的变量使用;通常应用①在硬件设备地址与c++的类直接关联;②容器也利用了预分配内存,然后逐步使用的方法;

void *p1 = (void*)new char[sizeof((A))];
A *p2 = new (p1)A();  //调用了无参构造函数,使用的是原来p1的大块内存;
                      //自定义有参的话,就可以使用A(12)带参数了;

    (2)内存池(池化技术是解决内存开销问题的,像线程池、内存池,其内部的机理是利用链表形成大内存,后面每次使用时,都分配一个结点空间给变量使用)

        ①作用:减少malloc次数,就减少了对内存的浪费;

        ②原理:用malloc分配一大块内存,当后面使用要分配时,从这一大块内存中一点一点分派,当一大块内存快用完时,再用malloc申请一大块内存,然后再一点一点分配;

        ③嵌入式指针,是借用A对象所占用的前8个字节(可能是2个整型数等),来充当指针,当被分配时,就指针后移,将该部分空间分配给新变量,利用内存共享,实现了空间的高效利用;

        内存池的代码实现:

#include <iostream>

using namespace std;

class Test
{
public:
    
    Test() = default;
    Test(int num)
    {
        m_num = num;
    }

    void * operator new(size_t size)
    {
        cout<<"size = "<<size<<endl;
        Test *temp;
        if(m_head == nullptr)
        {
            cout<<"malloc"<<endl;
            m_head = (Test *)malloc(sizeof(Test) * 50);
            temp = m_head;
            for(int i = 0; i < 50; i++)
            {
                temp->next = temp + 1;
                temp = temp->next;
            }
            temp->next = nullptr;
        }
        cout<<"mem"<<endl;
        temp = m_head;
        m_head = m_head->next;
        return m_head;
    }

    void * operator new[](size_t size)
    {
        cout<<"new[] "<<size<<endl;
        void *p = malloc(size);  //这里的size会自动转换为以字节为单位的大小
        return p;
    }

    void operator delete(void *p)
    {
        Test * temp = (Test *)p;
        temp->next = m_head;
        m_head = temp;
    }

    void operator delete[](void *p)
    {
        cout<<"delete[]"<<endl;
        free(p);
    }

    int m_t;
    int m_num;
    Test *next;
    static Test *m_head;
};

Test * Test::m_head = nullptr;

int main(int argc, char **argv)
{
    Test *p1 = new Test[10];  //这里的10指的是10个Test大小的内存

    delete [] p1;

    cout<<"begin:"<<endl;
    Test *p2 = new Test(2); //??这里是重载new,为什么当做构造函数报错?,因为Test(2)先构造一个对象,就像int(5)先初始化一个值为5的变量
    Test *p3 = new Test();   //为什么已添加这个就报错?
    p3->m_num = 3;
    p3->m_t = 4;
    cout<<p3->m_num<<" : "<<p3->m_t<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值