zoukankan      html  css  js  c++  java
  • C++基础——new与delete

    本文目录

    专业名称

    new operator:new操作符,new表达式
    operator new:new运算符
    placement new:定位new
    delete operator:delete操作符,delete表达式
    operator delete:delete运算符
    delete没有placement版本 各个版本的解释 1. new operator

    new operator:new表达式

    实际上只是一个称呼,是三个操作的集合。

    • 申请内存(调用operator new)
    • 在内存上调用构造函数
    • 将申请的内存空间返回

    delete operator:delete表达式

    delete operator也是一个称呼,是两个操作的集合。

    • 调用析构函数
    • 调用operator delete释放空间

    operator new/delete:new/delete运算符

    • 分为::operator new与Class::operator new。一个是全局的,一个是类内部定义的。平时听到的new与delete重载,就是重载这里的new/delete。注意点如下。
      -- class中的new与delete是隐式静态的。也就是不管你有没有声明为static,它都是static的。
      -- 非static成员在没有实例存在的情况下是不能使用的。而当我们定义一个实例时,很明显此时实例还没有存在,这个时候不可能调用一个非static的成员,所以operator new/delete必须是static的。
      -- static成员是不会隐式传入this指针的,所以在operator new/delete中不能使用任何类的非静态成员。
      -- 非静态的成员函数在编译器会在argument list的第一个位置插入一个本身类型的this指针。所以一个函数名,参数列表与全局函数一模一样的函数,也不会出现二义性问题。但是一个static成员函数,是不会隐私插入this指针的,所以当我们在class中重载new/delete时,客户定义实例时需要确认是否在全局也定义了new,否则会产生二义性。举个栗子。
    //VS2013
    class StringClass
    {
    public:
        void *operator new(size_t size);
    };
    void *operator new(size_t size);
    void main()
    {
        //1. 调用operator new申请内存
        //2. 调用构造函数
        //二义性,同时存在两个可用的operator new
        StringClass *p = new StringClass;
    }
    

    -- PS:关于函数匹配规则,一开始接触觉得很简单,当某天触发了bug的时候就会“书到用时方恨少”,这方面推荐两个连接给大家。看这里还有这里

    • 关于operator new/delete标准库提供了8个版本,这些版本的new/delete我们都是可以重载的。如下。
    //八个版本中有2个new版本有可能抛出异常
    void* operator new(size_t t);
    void* operator new[](size_t t);
    void* operator delete(void*)noexcept;
    void* operator delete[](void*)noexcept;
    
    //nothrow_t为空结构体,用于区分上面与下面运算符
    void* operator new(size_t t,nothrow_t&)noexcept;
    void* operator new(size_t t,nothrow_t&)noexcept;
    void* operator delete(void *,nothrow_t&)noexcept;
    void* operator delete[](void *,nothrow_t&)noexcept;
    

    理论这么多,下面先来个栗子。

    //StringClass.h
    //自己定义的一个类
    #pragma once
    #include<iostream>
    using namespace std;
    class StringClass
    {
    public:
        StringClass(){
            cout << "string class construtor" << endl;
        }
        ~StringClass(){
            cout << "string class destrutor" << endl;
        }
        void *operator new(size_t size){
            cout << "Class::operator new(size_t) " << size << endl;
            void *ret = (void*)malloc(size);
            return ret;
        }
        static void operator delete(void *p){
            free(p);
            cout << "Class::operator delete(void*)" << endl;
        }
    };
    void main(){
        StringClass *p = new StringClass;
        delete p;
    }
    

    结果如下。在new表达式中,先调用了operator new,再调用constructor,最后将指针返回给p。最后在释放的时候,先调用了destructor,再调用了operator delete。

    既然使用到了delete,怀着“授人以鱼不如授人以渔”的想法,推荐读者了解一下crtdbg这个头文件,谷歌一下就知道了。

    placement new:定位new

    • 在堆里面申请内存,需要寻找空的内存,多次申请释放还会产生大量的内存碎片,但这个神器会帮你解决这些问题。使用它,你只需要申请一次内存,就能够多次创建不同的实例,具体参见下例的讲解。
    • 前面提到,new operator会有三个步骤,第一是申请内存,第二是调用构造函数,第三是返回指针。定位new,之所以为定位,是因为我们事先分配好了内存,并且指定了必须使用这块内存进行初始化。共有四种语法如下。
    new (place_address) type;
    new (place_address) type (initializers);
    new (place_address) type[size];
    new (place_address) type[size]{braced initializers list};
    

    对第一种我们举个栗子。

    #pragma once
    #include<iostream>
    using namespace std;
    
    class StringClass
    {
    public:
        StringClass(){
            cout << "string class construtor" << endl;
        }
        ~StringClass(){
            cout << "string class destrutor" << endl;
        }
        //比起前面的代码,增添了这个函数
        static void *operator new(size_t size, void *p)    {
            cout << "Class::operator new(size_t,void*)" << endl;
            return p;
        }
    };
    
    void main()
    {
        StringClass *buf = (StringClass*)malloc(sizeof(StringClass));
        memset(buf, 0, sizeof(StringClass));
        StringClass *p2 = new(buf) StringClass();
        p2->~StringClass();
        free(buf);
    }
    
    • 上面总共有五个步骤:
      -- 上述代码先使用申请了一块内存,大小用户根据自己需要去决定。
      -- 初始化这块内存。
      -- 然后在上面调用了StringClass的构造函数,再令p2指向他。
      -- 然后p2使用完毕在上面调用自己的析构函数。
      -- 程序结束前将这块内存释放。假如有需要创建另外一个对象,我们可以不用申请新的内存,继续使用这块内存。

    • 与operator new不同的是,使用placement new是无需定义operator delete的,如上,整个过程压根就没有用到它。相反,假如你在客户端使用了它,还会导致调用析构函数的多次调用。

    警告

    除了placement new外,其他版本的new与delete都要成对地重载。因为你重载了编译器的new,它不知道delete需要为你做些什么。

    总结

    • new总共分成3类,new operator是一个操作的集合,不是具体的运算符。可以重载的叫做operator new。
    • 使用了placement new,必须定义void *operator new(siez_t,void *p),不用定义operator delete。而除了placement new,若定义了operator new/delete需要成对定义。
    • 所有的operator new都是隐式静态的,为了让代码易于阅读,你应该加上static
    • 所有的operator new都应该返回void*

    最后的疑问

    在很多书籍以及博客中,都指出了下面这个operator new不能被重载。在C++Primer第五版,白色封面的那本,P727最下方中,明确指出了它不能重载,但实际上,使用placement new的时候我们却不得不重载这个版本。关于这一点,希望有高人能够给出意见

  • 相关阅读:
    网络七层
    微信小程序开发工具 常用快捷键
    BZOJ 1026 windy数 (数位DP)
    BZOJ 1026 windy数 (数位DP)
    CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)
    CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)
    HDU 3709 Balanced Number (数位DP)
    HDU 3709 Balanced Number (数位DP)
    UVA 11361 Investigating Div-Sum Property (数位DP)
    UVA 11361 Investigating Div-Sum Property (数位DP)
  • 原文地址:https://www.cnblogs.com/suimeng/p/5295754.html
Copyright © 2011-2022 走看看