zoukankan      html  css  js  c++  java
  • 8. 了解各种不同意义的new 和 delete

    C++中关于new 的形态主要有三种:new operator,   operator new,   placement new

    三者的用法有着不同,注意区别适用的条件:

    new operator:

    new 操作符,和C++语言其它操作符(如+, -, ->, ::....)一样,是由语言内建的,总是完成相同的事情,程序员不能改变其意义。如下为new的一种用法:

    string *ps = new string("hazirguo");

    它主要完成的任务包括三个方面:

    1. 分配足够的内存,用来放置某类型对象。上例中分配足够放置一个string对象的内存。
    2. 调用一个constructor,为分配的内存的对象初始化。上例中就调用string类的构造函数string::string( const string &s );
    3. 返回相应的指针。上例中返回指向新建string对象的指针。

    注:new 和 sizeof 一样,是C++语言的关键字,不是函数,不能够被重载!

    operator new:

    上面new操作符完成的任务的第一点是调用函数执行分配内存动作,类似于C语言中malloc()函数的功能,你可以重载或重写此函数,改变其行为,这就是operator new 这个函数完成的功能。通常声明为:

    void * operator new (size_t size);

    参数size表示需要分配的内存的大小;函数的返回值为void *, 指向一块原始的、未初始化的内存。

    该函数可以被重载,但第一参数的类型必须总是size_t。全局的operator new 的重载形式有多种,你也可以根据自己的需要写出自己的重载函数,如:

    int *pnum = new int(5);   //使用new operator
    
    //下面自己写一个operator new重载形式,初始化分配的内存
    
    void *operator new (size_t size, int n)
    
    {
    
        void *tmp = ::operator new(size);       //使用全局operator new函数的一种重载形式分配内存
    
        *static_cast<int *>(tmp) = n;
    
        return tmp;
    
    }
    
    //调用自己版本的重载函数
    
    int *pnum = static_cast<int *>operator new(sizeof(int), 5);

    placement new:

    placement new 是operator new 下面的一个重载版本:

    void * operator new (size_t size, void * location)
    
    {
    
        return loaction;
    
    }

    完成在一个已经分配好的原始内存上构建一个新的对象并初始化。因为在已经分配好的内存空间上调用constructor进行对象初始化无意义,此时就用到placement new。使用前要加上头文件#include <new>,使用的方法如下:

    CTest* pT = new(p) CTest(...);   //在p指向的内存中调用CTest的构造函数来初始化CTest对象,并返回此对象的指针。
    举个实际的例子:
    char *buffer = new char[sizeof("hazirguo")+1];         //事先分配内存
    
    string *pch = new(buffer) string("hazirguo");          //完成对象的初始化

    小结:

    1. 如果希望将对象产生与堆上,使用new operator, 不但完成内存分配而且为该对象调用一个constructor。

    2. 如果只打算分配内存,而不希望任何constructor被调用,请调用new operator函数。

    3. 如果打算在已分配的内存中构造对象,请使用placement new。


    同理,对于delete也有类似的用法:

    delete operator:

    对应与new operator的释放动作,既能够析构指针所指的对象,又能够释放被该对象占用的内存。如:

    string *ps = new string("hazirguo"); 
    
    ...
    
    delete ps;        //使用的是delete operator
    
    //上句完成的功能近似于下面代码完成的功能:
    
    ps->~string();          //1.调用对象的dtor
    
    operator delete(ps);    //2.释放对象所占用的内存

    operator delete:

    完成内存释放动作的函数,通常声明如下:

    void operator delete(void *memoryToBeDeallocated);
    如果只打算处理原始的、未初始化的内存,应该完全回避new operator 和 delete operator,改调用operator new 取得内存并以operator delete归还给系统:
    void *buffer = operator new (50 * sizeof(char));    //分配放置50个char型的内存空间,没有任何ctor
    
    ...
    
    operator delete (buffer);                           //释放分配的内存,没有任何dtor

    完成的功能类似与C语言中的malloc 和 free 函数的功能。

    placement delete:

    如果是使用placement new, 在某内存块中产生对象,应该避免对那块内存使用delete operator。因为delete operator会调用operator delete来释放内存,但该内存内含的对象最初并非是由operator new分配而来的。毕竟placement new只是返回它所接受的指针而已,并不知道那个指针从哪里而来。所以为了抵消该对象的constructor的影响,应该直接调用该对象的destructor。

    举例:

    下面一个实例来源于网络,感觉很能说明问题,摘于此供理解:

    /* 
    
        F1 中的 new operator,他的行为就像F2中的 operator new 和 placement new 一样,
    
        也可以用F3中的方法达到相同的效果。
    
    */
    
    
    
    #include <new>
    
    #include <iostream>
    
    using namespace std;
    
    
    
    class CTest
    
    {
    
    public:
    
        CTest(int _x, int _y)
    
        {
    
            X = _x;
    
            Y = _y;
    
        }
    
        ~CTest()
    
        {
    
            X = 0;
    
            Y = 0;
    
        }
    
        void Test(char* sz)
    
        {
    
            printf("%s: X=%d Y=%d \n", sz, X, Y);
    
        }
    
        int X;
    
        int Y;
    
    };
    
    
    
     
    
     /*   new operator  && delete operator   */
    
    
    
    void F1()
    
    {
    
        CTest* pT = new CTest(1, 1);       // new operator
    
    
    
        pT->Test("F1");
    
    
    
        delete pT;                         // delete operator
    
    }
    
    
    
    /*   operator new + placement new   &&  operator delete + placement delete     */
    
    
    
    void F2()
    
    {
    
        void*  p  = operator new(sizeof(CTest));  // operator new : 分配内存
    
        CTest* pT = new(p) CTest(2, 2);           // placement new: 构造对象
    
        
    
        pT->Test("F2");
    
        
    
        pT->~CTest();                            // 必须显示析构对象
    
        operator delete(pT);                     // operator delete: 释放内存
    
    }
    
    
    
    
    
    /*    also can implement like this    */
    
    
    
    void F3()
    
    {
    
        char*  p  = new char[sizeof(CTest)]; // new operator: char为内置类型,不会调用构造函数,相当于只分配内存
    
        CTest* pT = new(p) CTest(3, 3);      // placement new: 在这块内存上构造CTest对象
    
        
    
        pT->Test("F3");
    
        
    
        pT->~CTest();                        // 必须显示析构CTest对象
    
        delete [] p;                         // delete operator: char为内置类型,不会调用析构函数,相当于只释放内存
    
    }
    
    
    
    void main()
    
    {
    
        F1();
    
        F2();
    
        F3();
    
    }
    

    前面都是对单对象,面对数组时,还有一些形式需要注意,如:

    string *ps = new string[10];        //分配一个对象数组
    此时,内存分配不再是operator new,而是名为operator new[]的函数负责,但是这个函数同样可以被重载。
    (。。。此处待补充)
     
    参考文献: 《More Effective C++ 35个改善编程与设计的有效方法 中文版》
  • 相关阅读:
    https://scrapingclub.com/exercise/detail_sign/
    https://scrapingclub.com/exercise/basic_captcha/
    https://scrapingclub.com/exercise/basic_login/
    344. 反转字符串(简单)
    142. 环形链表 II(中等)
    面试题02.07.链表相交
    19. 删除链表的倒数第 N 个结点
    24.两两交换链表中的节点
    206.反转链表(简单)
    707.设计链表
  • 原文地址:https://www.cnblogs.com/hazir/p/2450172.html
Copyright © 2011-2022 走看看