zoukankan      html  css  js  c++  java
  • C++内存管理-new,delete,new[],placement new的简单使用

    技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性

    首先,我们先看一下C++应用程序,使用memory的途径如下图所示

    C++应用程序中申请内存基于分配器的实现(std::allocator),而分配器基于C++primitives(new,new[]...),c++primitives基于C语言中的malloc/free..,当然越底层的函数效率越高.

    那我们会想,直接用最底层的实现多好,效率还高.但如果你直接调用底层的函数去实现功能,虽然你的效率提高了,但你的程序的可移植性就会相应的降低.

    不可否认底层语言的实现,体现出一定的个人能力.但过多的篇幅去实现底层语言,会影响开发效率.


    下面说一下C++三种申请内存的工具,然后介绍一下这三种工具的简单使用

    A.new operator, delete operator : 申请内存+构造函数

    B.new[], delete[] : 用于数组类的操作

    C.placement new : 定点new


    一 new operator, delete operator,

    分配内存的函数的对应关系如下图,下面主要介绍一下下面这些函数的简单使用

    int main()
    {
        void* p1 = malloc(512); //512bytes
        free(p1);
    
        complex<int>* p2 = new complex<int>; //one object
        delete p2;
    
        void* p3 = ::operator new(512);// 512bytes
        ::operator delete(p3);
    
        //一下函数都是non-static,一定要通过object调用,分配7个int的内存
        void* p4 = allocator<int>().allocate(7);//allocator<int>()创建临时对象
        allocator<int>().deallocate((int*)p4,7);
    
        return 0;
    }

    我们都知道 new = operator new + 构造函数,delete = 析构函数 + operator delete .如果以代码的形式表现出来应该是这样

    比如我们创建一个复数类,

    Complex* pc = new Complex(1,2);

    那么编译器会将上面的代码翻译成

        try
        {
            void* mem = operator new(sizeof(Complex)); //allocate
            
            pc = static_cast<Complex*>(mem); //cast
                    
            pc->Complex::Complex(1,2); //只有编译器可以直接调用构造函数
        
        }
        catch (std::bad_alloc)
        {
            //申请内存失败...
        }

    释放内存

    delete pc;

    编译器翻译为

        pc->~Complex();//先调用析构函数
        operator delete(pc);// operator delete的实现基于free()

    二 new[]和delete[]

    new[],主要运用于数组的申请内存,

    如class A 当我们调用

    A* p = new A[3];//那么就会申请3个class A的内存并调用3次 class A的构造函数

    当我们调用 delete[]时

    delete[] p; //就会调用3次析构函数 并释放内存(3个class A的内存)

    如果我们释放时没有加[] 

    delete p;

    它同样会释放3个class A的内存,但是之后调用其一个构造函数(如果其构造函数内有其他释放内存的操作,那么我们不加[]就会造成内存泄漏)

    看一下测试代码

        class A
        {
        public:
            int id;
    
            A() : id(0)
            {
                cout << "default ctor.this=" << this << " id=" << id << endl;
            }
    
            A(int i): id(0)
            {
                cout << "ctor.this=" << this << " id=" << id << endl;
            }
    
            ~A()
            {
                cout << "dtor.this=" << this << " id=" << id << endl;
            }
        };

    测试

        void test_class()
        {
            A* buf = new A[3]; //默认构造函数调用3次 调用顺序 0-1-2
                               //A必须有默认构造函数 引文new A[3]调用的是默认构造函数
    
            A* tmp = buf;//记录A数组的起点位置
    
            cout << "buf=" << buf << " tmp=" << tmp << endl;
    
            for(int i = 0; i < 3; i++)  
            {
                new(tmp++)A(i); //placement new
            }
    
            cout << "buf=" << buf << " tmp=" << tmp << endl;
    
            delete[] buf;
        }

    输出结果

    我们会发现 delete[] 的顺序与 new[] 的顺序相反,placement后面再说

    那么我们这个使用不写[]呢,看看输出结果会怎么样

    上面的delete 没有写[], 3个class A的内存是释放了,但是只调用了一次析构函数.


    三 placement new

    placement new允许我们将object创建与 已经申请好的内存中,但是没有所谓的 placenment delete,因为根本没有分配内存,所以没有placement delete

    但是有被称作的placement delete后面说.先看一下placement new

    char* buf = new char[sizeof(A) * 3];//申请了3个A的内存
    
    A* pc = new(buf)A();//运用申请好的buf的内存,在buf上赋值

     上面的new(buf)A();就是placement new.

    编译器遇到上面代码会翻译成

    A * pc;
    try {
        void* men = operator new(sizeof(A), buf); //借用内存
        pc = static_cast<A*>(mem);//安全转换
        pc->A::A();//构造函数
    }
    catch (std::bad_alloc){
        //若失败 不执行构造函数
    }

    以上就是三种C++申请内存工具的介绍

    参考侯捷<<C++内存管理>> 

  • 相关阅读:
    安卓 广播机制
    安卓 活动的启动模式
    安卓 生命周期
    安卓六大布局
    day4-list,列表
    Leetcode 947 移除最多的同行或同列石头
    Leetcode 628三个数的最大乘积
    Leetcode 1584连接所有点的最小费用
    Leetcode 721 账户合并
    Leetcode 103 二叉树的锯齿层序遍历
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/7726597.html
Copyright © 2011-2022 走看看