• c++中new的用法


    new operator

      内置的new操作符,经常使用的T *ptr = new T(),分配内存,调用构造函数

    1. 调用operator new分配内存,operator new (sizeof(A)) 
    2. 调用构造函数生成类对象,A::A() ,调用placement new
    3. 返回相应指针 

      事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),否则调用全局::operator new(size_t ),后者由C++默认提供。

    operator new

      像普通运算符一样可以被重载,operator new会去申请内存,申请失败的时候会调用new_handler处理,这是一个循环的过程,如果new_handler不抛出异常,会一直循环申请内存,直到成功。

    (1) void* operator new (std::size_t size);
    (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
    (3) void* operator new (std::size_t size, void* ptr) noexcept;
    1. 分配size字节的存储空间,如果成功的话返回一个非空指针,将对象类型进行内存对齐,指向分配空间第一个字节。如果失败的话,会抛出bad_alloc异常,不调用构造函数
    2. 和第一种一样,差别在于,如果失败的话,不抛出异常,而是返回一个null指针,不调用构造函数
    3. 只是返回ptr指针,并不分配内存空间。这里的ptr应该指向先前已经分配好的空间,这里的new调用对象的构造函数,在ptr指向的内存空间构造对象或对象数组。ptr指向的内存只要不释放,可以重复使用,所以这种用法一般在对象池或内存池实现中使用也就是placement new版本
    #include <iostream>
    #include <new>
    using namespace std;
    
    struct A
    {
        A( bool xpt )
        {
            if( xpt )
                throw( xpt );
        }
    
        void *operator new(size_t size)
        {
            cout<<"operator new"<<endl;
            return malloc(size);
        }
        void operator delete(void *p)
        {
            cout<<"operator delete"<<endl;
            free(p);
        }
      
    
        void *operator new(size_t size,const nothrow_t &thorw_value) noexcept
        {
            cout<<"operator new noexcept"<<endl;
            return malloc(size);
        }
        void operator delete(void *p,const nothrow_t &nowthrow_value) throw()
        {
            cout<<"operator delete noexpect."<<endl;
            free(p);
        }
    };
    
    int main()
    {
        A* a1 = new A(false);
        delete a1;
    
        try 
        {    
            A* a2 = new A(true);
            delete a2;
        }
        catch( ... )
        {
            // 
        }
      
    
        A* a3 = new(nothrow) A(false);
        delete a3;
    
        try 
        {
            A* a4 = new(nothrow) A(true);
            delete a4;
        }
        catch( ... )
        {
            
        }
        return 0;
    }

    在分配失败的情况下,抛出异常std::bad_alloc而不是返回NULL,因此通过判断返回值是否为NULL是徒劳的。

    placement new

      除了应该有的size_t size参数,多其他的任何参数都可以看做placement new

    1. 这种new允许在一块已经分配成功的内存上重新构造对象或对象数组。placement new不用担心内存分配失败,因为它根本不分配内存,它做的唯一一件事情就是调用对象的构造函数
    2. 用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。
    3. 使用语句A* p=new (mem) A;定位生成对象时,指针p和数组名mem指向同一片存储区。 会自动调用类A的构造函数,但是由于对象的空间不会自动释放(对象实际上是借用别人的空间),所以必须显示的调用类的析构函数,如本例中的p->~A()。 
    void* operator new (std::size_t size, void* ptr) noexcept;

      placement new构造起来的对象或其数组,要显示的调用他们的析构函数来销毁,千万不要使用delete ,要显式调用它们的析构函数来销毁(析构函数并不释放对象的内存,这是因为placement new构造起来的对象或数组大小并不一定等于原来分配的内存大小,使用delete会造成内存泄漏或者之后释放内存时出现运行时错误。

    #include <iostream>
    #include <new>
    #include <cstdio>
    using namespace std;
    
    struct A
    {
        char c;
        int i;
        short a;
    }; 
    
    int main()
    {
        A *a1=new A;
        A *a2=new(a1) A;//再堆上构造对象
        
        char *c={"fdsafk"};//在栈上构造对象 
        cout<<(void *)c<<endl;
        
        A *a3=new(c) A;
        cout<<a3<<endl;
        return 0;
    }

      当使用new运算符定义一个多维数组变量或数组对象时,它产生一个指向数组第一个元素的指针,返回的类型保持了除最左边维数外的所有维数。例如:  

     int *p1 = new int[10];   

      返回的是一个指向int的指针int*  

    int (*p2)[10] = new int[2][10]; 

      new了一个二维数组, 去掉最左边那一维[2], 剩下int[10], 所以返回的是一个指向int[10]这种一维数组的指针int (*)[10].  

      int (*p3)[2][10] = new int[5][2][10];  new了一个三维数组, 去掉最左边那一维[5], 还有int[2][10], 所以返回的是一个指向二维数组int[2][10]这种类型的指针int (*)[2][10].     

    #include<iostream>
    #include <typeinfo> 
    using namespace std;
    
    int main() 
    { 
        int *a = new int[34]; 
        int *b = new int[]; 
        int (*c)[2]=new int[][2];//指针 
        
        int[34][2]; 
        int (*d)[2] = new int[][2]; //指针 
        int (*e)[2][3] = new int[34][2][3];
        int (*f)[2][3] = new int[][2][3];//指针 
        
        a[0] = 1; 
        b[0] = 1; //运行时错误,无分配的内存,b只起指针的作用,用来指向相应的数据
        c[0][0] = 1;
        d[0][0] = 1;//运行时错误,无分配的内存,d只起指针的作用,用来指向相应的数据 
        e[0][0][0] = 1; 
        f[0][0][0] = 1;//运行时错误,无分配的内存,f只起指针的作用,用来指向相应的数据 
    
        cout<<typeid(a).name()<<endl;
        cout<<typeid(b).name()<<endl;
        cout<<typeid(c).name()<<endl; 
        cout<<typeid(d).name()<<endl; 
        cout<<typeid(e).name()<<endl;
        cout<<typeid(f).name()<<endl; 
        delete[] a; 
        delete[] b; 
        delete[] c; 
        delete[] d; 
        delete[] e; 
        delete[] f; 
        return 0;
    }   
     

     

  • 相关阅读:
    数据库开发及ADO.NET
    C#典型案例及分析
    Visual Studio 2017 常用快捷键
    C#方法(函数)
    Linux常用命令----RPM包管理
    Linux常用命令----VIM命令
    Linux常用命令----压缩解压命令
    Linux常用命令----用户管理命令
    文件搜索命令
    Linux常用命令----权限管理命令
  • 原文地址:https://www.cnblogs.com/tianzeng/p/8964215.html
Copyright © 2020-2023  润新知