四万字长文说operator new & operator delete

1.newdelete的工作原理

C++允许重定义程序中内存分配和释放的方式,既可以在全局层次也可以在类层次。当需要编写一个内存池分配器的时候可以使用此技术。在类层次对operator newoperator delete进行重载是有价值的,但还是别在全局层次炫技了吧。

new表达式与operator new函数
标准库函数operator newoperator delete的名字容易让人误解。和其它operator函数不同(比如operator=),这两个函数并没有重载new表达式或delete表达式。
实际上,我们根本无法自定义new表达式或delete表达式的行为
可以重载operator newoperator delete来控制内存的分配和释放,但不能重载new表达式和delete表达式。也就是说,可以自定义的是实际的内存分配和释放,但不能自定义构造函数和析构函数的调用。
我们提供新的operator new函数和operator delete函数的目的在于改变内存分配的方式,但是不管怎样,都不能改变new运算符和delete运算符的基本含义。

一条new表达式的执行过程总是分为三步:

  1. new表达式调用一个名为operator new(或operator new[])的标准库函数,该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组);
  2. 编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
  3. 对象被分配了空间并构造完成,返回一个指向该对象的指针。
// new表达式
std::string *sp = new std::string("a value");  // 分配并初始化一个std::string对象
std::string *arr = new std::string[10];        // 分配10个默认初始化的std::string对象

一条delete表达式执行过程总是分为两步:

  1. 对所指对象或者对象数组执行对应的析构函数;
  2. 编译器调用名为operator deleteoperator delete[]的标准库函数释放内存空间。
// delete表达式
delete sp;          // 销毁*sp,然后释放sp指向的内存空间
delete [] arr;      // 销毁数组中的元素,然后释放对应的内存空间

2.标准库定义的operator newoperator delete

标准库定义了6种不同形式的new表达式,每种形式都有对应的operator new
只有2种形式的delete表达式可以调用:deletedelete[],没有nothrowplacement的形式。然而有6种形式的operator delete

不同编译器(工具链)对operator newoperator delete实现的个数和形式是不一样的,因此实际使用时请查看对于特定编译器(工具链)的具体实现。

以Visual Studio 2017来展示标准库里对operator newoperator delete的定义,图1是版本信息:

四万字长文说operator new & operator delete_第1张图片
图1 Visual Studio 2017版本信息

Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\Linux\include\usr\include\c++\5\new

// The -*- C++ -*- dynamic memory management header.

// Copyright (C) 1994-2015 Free Software Foundation, Inc.

// This file is part of GCC.
//
// GCC is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3, or (at your option)
// any later version.
// 
// GCC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// .

/** @file new
 *  This is a Standard C++ Library header.
 *
 *  The header @c new defines several functions to manage dynamic memory and
 *  handling memory allocation errors; see
 *  http://gcc.gnu.org/onlinedocs/libstdc++/18_support/howto.html#4 for more.
 */

#ifndef _NEW
#define _NEW

#pragma GCC system_header

#include 
#include 

#pragma GCC visibility push(default)

extern "C++" {

namespace std 
{
  /**
   *  @brief  Exception possibly thrown by @c new.
   *  @ingroup exceptions
   *
   *  @c bad_alloc (or classes derived from it) is used to report allocation
   *  errors from the throwing forms of @c new.  */
  class bad_alloc : public exception 
  {
  public:
    bad_alloc() throw() { }

    // This declaration is not useless:
    // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
    virtual ~bad_alloc() throw();

    // See comment in eh_exception.cc.
    virtual const char* what() const throw();
  };

#if __cplusplus >= 201103L
  class bad_array_new_length : public bad_alloc
  {
  public:
    bad_array_new_length() throw() { };

    // This declaration is not useless:
    // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
    virtual ~bad_array_new_length() throw();

    // See comment in eh_exception.cc.
    virtual const char* what() const throw();
  };
#endif

  struct nothrow_t { };

  extern const nothrow_t nothrow;

  /** If you write your own error handler to be called by @c new, it must
   *  be of this type.  */
  typedef void (*new_handler)();

  /// Takes a replacement handler as the argument, returns the
  /// previous handler.
  new_handler set_new_handler(new_handler) throw();

#if __cplusplus >= 201103L
  /// Return the current new handler.
  new_handler get_new_handler() noexcept;
#endif
} // namespace std

//@{
/** These are replaceable signatures:
 *  - normal single new and delete (no arguments, throw @c bad_alloc on error)
 *  - normal array new and delete (same)
 *  - @c nothrow single new and delete (take a @c nothrow argument, return
 *    @c NULL on error)
 *  - @c nothrow array new and delete (same)
 *
 *  Placement new and delete signatures (take a memory address argument,
 *  does nothing) may not be replaced by a user's program.
*/
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
  __attribute__((__externally_visible__));

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }

// Default placement versions of operator delete.
inline void operator delete  (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }
//@}
} // extern "C++"

#pragma GCC visibility pop

#endif

operator new有可抛出异常版本(默认)和不可抛出异常版本。 需要注意的是,nothrow new不抛出异常只适用于内存分配,后续的构造函数调用还是可能抛出异常
与析构函数类似,operator delete也不允许抛出异常。当重载operator delete的不同版本时必须使用noexcept异常说明符指定其不抛出异常。C++标准指出从delete抛出异常的行为是未定义的。

对于operator new函数或者operator new[]函数来说,返回类型必须是void*,第一个形参类型必须是size_t且该形参不能含有默认实参。当编译器调用operator new时,把存储指定类型对象所需的字节数传给size_t形参;当调用operator new[]时,传入函数的则是存储数组重所有元素所需的空间。

3.重载operator newoperator delete时也会出现类似构造函数的隐藏效应

当自定义实现重载抛异常版本void* operator new(size_t size)或不抛异常版本void* operator new(size_t size, const std::nothrow_t&) noexcept的其中一个时,编译器都会隐藏标准库的版本,即编译器会使用自定义的版本替换标准库的版本。这与类中自己编写了构造函数时会隐藏默认构造函数类似。假设对于上述两种原型重载了其中一种形式但实际使用的是另一种形式,编译器会报错。因此重载哪个版本只能使用哪个版本。

对于单个元素的版本之间的影响和数组的版本之间的影响都是如此,但单个元素版本与数组版本之间无此影响。详见下面小章节的示例。由于运行时对待operator newoperator delete时不太一样的,重载的operator new基本都可以被主动调用,但重载的operator delete正常情况下仅能调用原型是void operator delete(void* ptr) noexcept的版本,章节4会进一步介绍。

3.1 单个元素,重载抛异常版本,不重载不抛异常版本,但企图使用不抛异常版本

注意,本篇文章仅讨论自定义版本的operator newoperator delete如何使用,不讨论operator newoperator delete函数的实现细节。因此本篇文章的代码示例并未遵守一些约定,比如operator new都应该内含一个循环,反复调用某个new handler。详见参考文献3的条款49和条款51

#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *p2 = new(std::nothrow) MyMemory(); // [ERROR] error: no matching function for call to 'test_operator_new::MyMemory::operator new(sizetype, const std::nothrow_t&)'
        delete p2;
        p2 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}
3.2 单个元素,不重载抛异常版本,重载不抛异常版本,但企图使用抛异常版本
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        //virtual ~MyMemory(){
        //    std::cout << "MyMemory dtor called." << std::endl;
        //}

        /*索引2,单个元素,不抛出异常版本,开始****************************************************************/
        void* operator new(size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void* operator new(size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete(void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void operator delete(void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,不抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *p2 = new MyMemory();  // [ERROR] error: no matching function for call to 'test_operator_new::MyMemory::operator new(sizetype)'
        delete p2;                      // [ERROR] error: no suitable 'operator delete' for 'test_operator_new::MyMemory'
        p2 = nullptr;

 		std::cout << "------------------------------" << std::endl;       
    }
}

上述代码比章节3.1中多了delete p2;处的报错,这是因为编译器默认是调用void operator delete(void* ptr) noexcept形式的operator delete,即使重载了void* operator new(size_t size, const std::nothrow_t&) noexcept。在章节4会进一步阐述。

注意示例代码中为了清晰地展示因为隐藏原因导致的编译错误将析构函数virtual ~MyMemory(){std::cout << "MyMemory dtor called." << std::endl;}注释掉了。因为不注释掉会再增加一处错误,详见章节4。

3.3 数组,重载抛异常版本,不重载不抛异常版本,但企图使用不抛异常版本
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引3,数组,抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size) {
            std::cout << "index 3, \"void* operator new[](size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete[](void* ptr) noexcept {
            std::cout << "index 3, \"void operator delete[](void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        MyMemory *p1 = new(std::nothrow) MyMemory[2]; // [ERROR] error: no matching function for call to 'test_operator_new::MyMemory::operator new [](sizetype, const std::nothrow_t&)'
        delete [] p1;
        p1 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}
3.4 数组,不重载抛异常版本,重载不抛异常版本,但企图使用抛异常版本
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引4,数组,不抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void* operator new[](size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void operator delete[](void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,不抛出异常版本,结束****************************************************************/

    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        MyMemory *p1 = new MyMemory[2]; // [ERROR] error: no matching function for call to 'test_operator_new::MyMemory::operator new [](sizetype)'
        delete [] p1;                   // [ERROR]  error: no suitable 'operator delete []' for 'test_operator_new::MyMemory'
        p1 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}
3.5 单个元素版本的重载与数组版本的重载互不影响

单个元素版本抛异常、不抛异常与数组版本抛异常、不抛异常的组合较多,以两种可能的组合来说明其它应该类似,不多枚举。

3.5.1 重载了单个元素的抛异常版本,没重载单个元素的不抛异常版本,不影响数组版本抛异常版本和不抛异常版本的使用。
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        MyMemory *p1 = new MyMemory[2];
        delete [] p1;
        p1 = nullptr;
        std::cout << "++++++++++\n";
                
        MyMemory *p2 = new(std::nothrow) MyMemory[2];
        delete [] p2;
        p2 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}

输出(数组版本均使用的是标准库的版本):

testing operator new & operator delete...
MyMemory ctor called.
MyMemory ctor called.
MyMemory dtor called.
MyMemory dtor called.
++++++++++
MyMemory ctor called.
MyMemory ctor called.
MyMemory dtor called.
MyMemory dtor called.
------------------------------
The end.
3.5.2 不重载数组的抛异常版本,重载数组的不抛异常版本,不影响单个单个元素抛异常和不抛异常版本的使用
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引4,数组,不抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void* operator new[](size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void operator delete[](void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,不抛出异常版本,结束****************************************************************/

    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        MyMemory *p1 = new MyMemory();
        delete p1;
        p1 = nullptr;
        std::cout << "++++++++++\n";

        MyMemory *p2 = new(std::nothrow) MyMemory();
        delete p2;
        p2 = nullptr;

 		std::cout << "------------------------------" << std::endl;       
    }
}

输出:

testing operator new & operator delete...
MyMemory ctor called.
MyMemory dtor called.
++++++++++
MyMemory ctor called.
MyMemory dtor called.
------------------------------
The end.

4.正常情况下,析构函数只调用原型是void operator delete(void* ptr) noexceptoperator delete

那其它版本的对operator delete的重载什么时候调用呢?详见4.3小章节吧。

4.1 void operator delete(void* ptr) noexcept一定是个宠儿

单个元素版本示例

#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/

        /*索引2,单个元素,不抛出异常版本,开始****************************************************************/
        void* operator new(size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void* operator new(size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete(void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void operator delete(void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,不抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        MyMemory *p1 = new MyMemory();
        delete p1;
        p1 = nullptr;
        std::cout << "++++++++++\n";
        
        MyMemory *p2 = new(std::nothrow) MyMemory();
        delete p2; // 注意此处会调用:index 1, "void operator delete(void* ptr) noexcept" called.
        p2 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}

输出:

testing operator new & operator delete...
index 1, "void* operator new(size_t size)" called.
MyMemory ctor called.
MyMemory dtor called.
index 1, "void operator delete(void* ptr) noexcept" called.
++++++++++
index 2, "void* operator new(size_t size, const std::nothrow_t&) noexcept" called.
MyMemory ctor called.
MyMemory dtor called.
index 1, "void operator delete(void* ptr) noexcept" called.
------------------------------
The end.

上面代码中对于单个元素的operator delete抛异常版本和不抛异常版本都有重载,p2在内存申请的使用使用的是不抛异常版本,但是内存释放的时候使用的不是void operator delete(void* ptr, const std::nothrow_t&) noexcept,而是void operator delete(void* ptr) noexcept

数组版本示例

#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() try {
            std::cout << "MyMemory ctor called." << std::endl;
            //throw std::runtime_error("hahaha...");
        }
        catch(...) {
            std::cout << "Caught MyMemory's exception." << std::endl;
            throw std::runtime_error("MyMemory's ctor caught exception.");
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引3,数组,抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size) {
            std::cout << "index 3, \"void* operator new[](size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete[](void* ptr) noexcept {
            std::cout << "index 3, \"void operator delete[](void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,抛出异常版本,结束****************************************************************/
        
        /*索引4,数组,不抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void* operator new[](size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void operator delete[](void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,不抛出异常版本,结束****************************************************************/        
    };
    
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        try {
            MyMemory *p1 = new MyMemory[2];
            delete [] p1;
            p1 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            // return 1;
        }
        std::cout << "++++++++++\n";

        try {
            MyMemory *p2 = new(std::nothrow) MyMemory[2];
            delete [] p2; // 此处调用void operator delete[](void* ptr) noexcept的版本
            p2 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            return 1;
        }
        
 		std::cout << "------------------------------" << std::endl;
        return 0;
    }
}

上面程序输出:

testing operator new & operator delete...
index 3, "void* operator new[](size_t size)" called.
MyMemory ctor called.
MyMemory ctor called.
MyMemory dtor called.
MyMemory dtor called.
index 3, "void operator delete[](void* ptr) noexcept" called.
++++++++++
index 4, "void* operator new[](size_t size, const std::nothrow_t&) noexcept" called.
MyMemory ctor called.
MyMemory ctor called.
MyMemory dtor called.
MyMemory dtor called.
index 3, "void operator delete[](void* ptr) noexcept" called.
------------------------------
The end.
4.2 只要重载operator delete那就必须有原型是 void operator delete(void* ptr) noexcept的版本,否则编译不过

下面示例说明只要重载operator delete那就必须有原型是 void operator delete(void* ptr) noexcept的版本,实际上也必须使用这个版本,哪怕重载了其它版本,就是这么霸道!!!

#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }    // [ERROR] error: no suitable 'operator delete' for 'test_operator_new::MyMemory'

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
#if 0
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
#endif
        /*单个元素,抛出异常版本,结束****************************************************************/

        /*索引2,单个元素,不抛出异常版本,开始****************************************************************/
        void* operator new(size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void* operator new(size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete(void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void operator delete(void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,不抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> void {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        // 别忘了delete p的过程:调用析构函数、释放内存
        
        MyMemory *p1 = new MyMemory();
        delete p1;   // [ERROR] error: no suitable 'operator delete' for 'test_operator_new::MyMemory'
        p1 = nullptr;
        std::cout << "++++++++++\n";
        
        MyMemory *p2 = new(std::nothrow) MyMemory();
        delete p2;   // [ERROR] error: no suitable 'operator delete' for 'test_operator_new::MyMemory'
        p2 = nullptr;
        
 		std::cout << "------------------------------" << std::endl;       
    }
}

上面代码是注释掉原型是void operator delete(void* ptr) noexcept版本的实现,编译会报错,错误如注释所示。也就是说只要重载operator delete,正常情况下就必须使用void operator delete(void* ptr) noexcept的版本,构造函数抛出异常时才使用与operator new配套的版本。

4.3 难道void operator delete(void* ptr, const std::nothrow_t&) noexcept的版本是摆设?

void operator delete(void* ptr, const std::nothrow_t&) noexcept的版本不是摆设,只有在构造函数抛出了异常的时候才会用到

参考文献《C++高级编程(第2版)》P594说,4种(分别是参数中有std::nothrow_t和没有的单个元素版本、数组版本)nothrowplacement形式的operator delete只有在构造函数抛出异常时才会使用。在这种情况下,匹配调用构造函数之前分配内存时使用的operator newoperator delete会被调用。然而,正常删除一个指针,delete会调用operator deleteoperator delete[](绝对不会调用nothrowplacement的形式)。

4.3.1 单个元素版本示例
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() try {
            std::cout << "MyMemory ctor called." << std::endl;
            throw std::runtime_error("hahaha...");
        }
        catch(...) {
            std::cout << "Caught MyMemory's exception." << std::endl;
            throw std::runtime_error("MyMemory's ctor caught exception.");
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/

        /*索引2,单个元素,不抛出异常版本,开始****************************************************************/
        void* operator new(size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void* operator new(size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete(void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 2, \"void operator delete(void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,不抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        try {
            MyMemory *p1 = new MyMemory();
            delete p1;
            p1 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            // return 1;
        }
        std::cout << "++++++++++\n";

        try {
            MyMemory *p2 = new(std::nothrow) MyMemory();
            delete p2; // 此处调用了void operator delete(void* ptr, const std::nothrow_t&) noexcept
            
            p2 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            return 1;
        }
        
 		std::cout << "------------------------------" << std::endl;
        return 0;
    }
}

输出:

testing operator new & operator delete...
index 1, "void* operator new(size_t size)" called.
MyMemory ctor called.
Caught MyMemory's exception.
index 1, "void operator delete(void* ptr) noexcept" called.
Caught exception in main.
++++++++++
index 2, "void* operator new(size_t size, const std::nothrow_t&) noexcept" called.
MyMemory ctor called.
Caught MyMemory's exception.
index 2, "void operator delete(void* ptr, const std::nothrow_t&) noexcept" called.
Caught exception in main.
The end.
4.3.2 数组版本示例
#include 
#include 
#include 

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() try {
            std::cout << "MyMemory ctor called." << std::endl;
            throw std::runtime_error("hahaha...");
        }
        catch(...) {
            std::cout << "Caught MyMemory's exception." << std::endl;
            throw std::runtime_error("MyMemory's ctor caught exception.");
        }
        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引3,数组,抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size) {
            std::cout << "index 3, \"void* operator new[](size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete[](void* ptr) noexcept {
            std::cout << "index 3, \"void operator delete[](void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,抛出异常版本,结束****************************************************************/
        
        /*索引4,数组,不抛出异常版本,开始****************************************************************/
        void* operator new[](size_t size, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void* operator new[](size_t size, const std::nothrow_t&) noexcept\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                return nullptr;
            }
        }
        void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
            std::cout << "index 4, \"void operator delete[](void* ptr, const std::nothrow_t&) noexcept\" called.\n";
            free(ptr);
        }
        /*数组,不抛出异常版本,结束****************************************************************/        
    };
    
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;
        
        try {
            MyMemory *p1 = new MyMemory[2];
            delete [] p1;
            p1 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            // return 1;
        }
        std::cout << "++++++++++\n";

        try {
            MyMemory *p2 = new(std::nothrow) MyMemory[2];
            delete [] p2; // 此处调用了void* operator new(size_t size, const std::nothrow_t&) noexcept
            p2 = nullptr;
        }
        catch(...) {
            std::cout << "Caught exception in main." << std::endl;
            return 1;
        }
        
 		std::cout << "------------------------------" << std::endl;
        return 0;
    }
}

输出:

testing operator new & operator delete...
index 3, "void* operator new[](size_t size)" called.
MyMemory ctor called.
Caught MyMemory's exception.
index 3, "void operator delete[](void* ptr) noexcept" called.
Caught exception in main.
++++++++++
index 4, "void* operator new[](size_t size, const std::nothrow_t&) noexcept" called.
MyMemory ctor called.
Caught MyMemory's exception.
index 4, "void operator delete[](void* ptr, const std::nothrow_t&) noexcept" called.
Caught exception in main.
The end.

5.重载operator newoperator delete

可以在全局作用域中定义operator new函数和operator delete函数(包含申请单个元素的版本和数组的版本),也可以将它们定义为成员函数。
当将上述运算符函数定义成类的成员时,它们是隐式静态的,无须显示声明static(这么做也不会引发错误)。因为operator new用在对象构造之前而operator delete用在对象销毁之后,所以这两个成员(newdelete)必须时静态的,而且它们不能操纵类的任何数据成员。

如果被分配(释放)的对象是类类型
(a)编译器首先在类及其基类的作用域中查找,如果该类含有operator new成员或operator delete成员,则相应的表达式将调用这些成员。否则,(b)编译器在全局作用域查找匹配的函数,如果编译器找到了用户自定义的版本,则使用该版本执行new表达式或delete表达式。(c )如果没有找到,则使用标准库定义的版本。

务必注意,Bjarne Stroustrup说“替换全局的operator newoperator delete时需要胆量的”。然而如果执迷不悟,一定要注意在operator new的实现代码中不要对new进行任何调用,否则会产生无限循环。

5.1标准库可被重载的8个函数的重载示例
#include 
#include 
#include 
#include 

namespace test_operator_new {
	class MyMemory {
	public:
		MyMemory() try : pstr_(new std::string){    // 使用标准库的new
			std::cout << "MyMemory ctor called.\n";
		}
		catch(std::bad_alloc& e) {
			std::cerr << "bad_alloc caught in MyMemory's ctor, " << e.what() << std::endl;
			throw /*std::bad_alloc()*/;
		}
		virtual ~MyMemory() {
			delete pstr_;       // 使用标准库的delete
			pstr_ = nullptr;
			
			std::cout << "MyMemory dtor called.\n";
		}
		
		void reset() {
			std::cout << "reseting...\n";
			delete pstr_;                                // 使用标准库的delete
			pstr_ = new std::string("The reset one.");   // 使用标准库的new
			std::cout << "reset done.\n";
		}
		
		void print() {
			if(pstr_) std::cout << *pstr_ << std::endl;
		}

		/* self-define operator new & operator delete, 这些函数不管加不加关键字static都默认都是static */
		/*static*/ void* operator new(size_t size) {
			std::cout << "in-class self-define operator new called.\n";
			
			return ::operator new(size);
		}
		/*static*/ void operator delete(void* ptr) noexcept{
			std::cout << "in-class self-define operator delete called.\n";

			::operator delete(ptr);
		}
		/*static*/ void* operator new[](size_t size) {
			std::cout << "in-class self-define operator new[] called.\n";
			
			return ::operator new[](size);
		}
		/*static*/ void operator delete[](void* ptr) noexcept{
			std::cout << "in-class self-define operator delete[] called.\n";

			::operator delete[](ptr);
		}

		
		/*static*/ void* operator new(size_t size, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator new called.\n";
			
			return ::operator new(size, std::nothrow);
		}
		/*static*/ void operator delete(void* ptr, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator delete called.\n";
			
			::operator delete(ptr, std::nothrow);
		}
		/*static*/ void* operator new[](size_t size, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator new[] called.\n";
			
			return ::operator new[](size, std::nothrow);
		}
		/*static*/ void operator delete[](void* ptr, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator delete[] called.\n";
			
			::operator delete[](ptr, std::nothrow);
		}

	protected:
		std::string *pstr_;
	};
	
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

		MyMemory mymem;
		std::cout << "+++++\n";
		
		MyMemory *pmymem1 = ::new MyMemory();   // 使用标准库的new
		::delete pmymem1;                       // 使用标准库的delete
		//delete pmymem1;                       // 使用in-class self-define delete,
		                                        // 运行时不会因为new的时候使用的是标准库的new来自动找对应标准库的delete,
												// 想使用哪个版本的new和delete都必须要手动指定,但最好一一对应,即要么都使用标准库的版本要么都使用自定义版本
		std::cout << "+++++\n";
		
		MyMemory *pmymem2 = new MyMemory();     // 使用in-class self-define new
		pmymem2->reset();
		pmymem2->print();		
		delete pmymem2;                         // 使用in-class self-define delete
		
		std::cout << "+++++\n";
        MyMemory *pmymem3 = new(std::nothrow) MyMemory[3];
        delete [] pmymem3;

		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

以上程序输出:

__cplusplus: 201703
testing operator new & operator delete...
MyMemory ctor called.
+++++
MyMemory ctor called.
MyMemory dtor called.
+++++
in-class self-define operator new called.
MyMemory ctor called.
reseting...
reset done.
The reset one.
MyMemory dtor called.
in-class self-define operator delete called.
+++++
in-class self-define noexcept version operator new[] called.
MyMemory ctor called.
MyMemory ctor called.
MyMemory ctor called.
MyMemory dtor called.
MyMemory dtor called.
MyMemory dtor called.
in-class self-define operator delete[] called.
------------------------------
MyMemory dtor called.
The end.
5.2不允许重载void* operator new(size_t, void*);的版本

void* operator new(size_t, void*);的形式只能供标准库使用,只能供标准库使用,只能供标准库使用,不能被用户重定义。
placement new(定位new)的形式如下:

new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] {braced initializer list}

其中place_address必须是一个指针,同时在initializers中提供一个(可能为空的)以逗号分隔符的初始值列表,该初始值列表将用于构造新分配的对象。

当只传入一个指针类型的实参时,placement new表达式构造对象但不分配内存。

以下实验说明可以重载此种形式,但是运行结果不符合预期。最重要的是虽然可以重载void* operator new(size_t, void*);的形式,但函数怎么实现呢?浅拷贝还是什么?不清楚。只知道该函数不分配任何内存(因此下面的代码示例关于palcement new的重载是不对的),它只是简单地返回指针实参。(注意,下面的代码的内部实现仅是展示重载的示例)。

#include 
#include 
#include 
#include 
#include 

namespace test_operator_new {
	class MyMemory {
	public:
		MyMemory() try : pstr_(new std::string){    // 使用标准库的new
			std::cout << "MyMemory ctor called.\n";
		}
		catch(std::bad_alloc& e) {
			std::cerr << "bad_alloc caught in MyMemory's ctor, " << e.what() << std::endl;
			throw /*std::bad_alloc()*/;
		}
		virtual ~MyMemory() {
			delete pstr_;       // 使用标准库的delete
			pstr_ = nullptr;
			
			std::cout << "MyMemory dtor called.\n";
		}
		
		void reset() {
			std::cout << "reseting...\n";
			delete pstr_;                                // 使用标准库的delete
			pstr_ = new std::string("The reset one.");   // 使用标准库的new
			std::cout << "reset done.\n";
		}
		
		void print() {
			if(pstr_) std::cout << *pstr_ << std::endl;
		}

		/* self-define operator new & operator delete, 这些函数不管加不加关键字static都默认都是static */
		/*static*/ void* operator new(size_t size) {
			std::cout << "in-class self-define operator new called.\n";
			
			return ::operator new(size);
		}
		/*static*/ void operator delete(void* ptr) noexcept{
			std::cout << "in-class self-define operator delete called.\n";

			::operator delete(ptr);
		}
		/*static*/ void* operator new[](size_t size) {
			std::cout << "in-class self-define operator new[] called.\n";
			
			return ::operator new[](size);
		}
		/*static*/ void operator delete[](void* ptr) noexcept{
			std::cout << "in-class self-define operator delete[] called.\n";

			::operator delete[](ptr);
		}

		
		/*static*/ void* operator new(size_t size, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator new called.\n";
			
			return ::operator new(size, std::nothrow);
		}
		/*static*/ void operator delete(void* ptr, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator delete called.\n";
			
			::operator delete(ptr, std::nothrow);
		}
		/*static*/ void* operator new[](size_t size, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator new[] called.\n";
			
			return ::operator new[](size, std::nothrow);
		}
		/*static*/ void operator delete[](void* ptr, const std::nothrow_t&) noexcept{
			std::cout << "in-class self-define noexcept version operator delete[] called.\n";
			
			::operator delete[](ptr, std::nothrow);
		}

        // 《C++ Primer》P727说void* operator new(size_t, void*);的版本不允许重新定义
		/*static*/ void* operator new(size_t size, void*ptr) noexcept{
			std::cout << "in-class self-define placement operator new called.\n";
			
			return ::operator new(size, ptr);
		}
		/*static*/ void operator delete(void*ptr, void*p) noexcept{
			std::cout << "in-class self-define placement operator delete called.\n";
			
			::operator delete (ptr, p);
		}
		/*static*/ void* operator new[](size_t size, void*ptr) noexcept{
			std::cout << "in-class self-define placement operator new[] called.\n";
			
			return ::operator new[](size, ptr);
		}
		/*static*/ void operator delete[](void*ptr, void*p) noexcept{
			std::cout << "in-class self-define placement operator delete[] called.\n";
			
			::operator delete[](ptr, p);
		}

	protected:
		std::string *pstr_;
	};
	
	
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

		std::cout << "+++++\n";
        MyMemory *pmymem4 = new(std::nothrow) MyMemory();
        MyMemory *pmymem5 = new(pmymem4)MyMemory();
        printf("pmymem4 = %p, pmymem5 = %p\n", pmymem4, pmymem5);
        pmymem5->reset();
        pmymem4->print();
        delete pmymem5;
        pmymem5 = nullptr;
        // pmymem4->print();  // 运行时会崩溃
        printf("pmymem4 = %p, pmymem5 = %p\n", pmymem4, pmymem5);
        
        if(nullptr == pmymem4){
            std::cout << "pmymem4 is nullptr.\n";
        }
        if(nullptr == pmymem5){
            std::cout << "pmymem5 is nullptr.\n";
        }

		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

以上程序输出(结果和预期不同):

__cplusplus: 201703
testing operator new & operator delete...
+++++
in-class self-define noexcept version operator new called.
MyMemory ctor called.
in-class self-define placement operator new called.
MyMemory ctor called.
pmymem4 = 00000264f10727c0, pmymem5 = 00000264f10727c0
reseting...
reset done.
The reset one.
MyMemory dtor called.
in-class self-define operator delete called.
pmymem4 = 00000264f10727c0, pmymem5 = 0000000000000000
pmymem5 is nullptr.
------------------------------
The end.
5.3重载其它形式的operator newoperator delete

定义带有额外参数的operator new时,还应该定义对应的带有额外参数的operator delete。但不能自己调用这个带有额外参数的operator delete,只有在使用了带有额外参数的operator new且对象的构造函数抛出了异常的时候才会调用这个operator delete

#include 
#include 
#include 
#include 

namespace test_operator_new {
	class MyMemory {
	public:
		MyMemory() try : pstr_(new std::string){    // 使用标准库的new
			std::cout << "MyMemory ctor called.\n";
            //throw std::runtime_error("ddd");
		}
		catch(std::bad_alloc& e) {
			std::cerr << "bad_alloc caught in MyMemory's ctor, " << e.what() << std::endl;
			throw /*std::bad_alloc()*/;
		}
		virtual ~MyMemory() {
			delete pstr_;       // 使用标准库的delete
			pstr_ = nullptr;
			
			std::cout << "MyMemory dtor called.\n";
		}
		
		void reset() {
			std::cout << "reseting...\n";
			delete pstr_;                                // 使用标准库的delete
			pstr_ = new std::string("The reset one.");   // 使用标准库的new
			std::cout << "reset done.\n";
		}
		
		void print() {
			if(pstr_) std::cout << *pstr_ << std::endl;
		}

        void* operator new(size_t size, int extra) {
			std::cout << "in-class self-define operator new (using malloc) called.\n";
			if(void *mem = malloc(size)) {
                return mem;
            }
            else{
                throw std::bad_alloc();
            }
        }
        void operator delete(void *mem/*, int extra*/) noexcept {
			std::cout << "in-class self-define operator delete (using free) called.\n";

			free(mem);
        }
        
        void* operator new(size_t size, const std::string& debug_info ) {
			std::cout << "The following is new debug infomation: " << debug_info << std::endl;
			if(void *mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void *mem, const std::string& debug_info) noexcept{
			std::cout << "The following is delete debug infomation: " << debug_info << std::endl;

			free(mem);
        }

	protected:
		std::string *pstr_;
	};
	
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *pmem = new(4) MyMemory();
        delete pmem;
        pmem = nullptr;
        
        MyMemory *pmem1 = new(std::string("Pyramid")) MyMemory();
        delete pmem1;
        pmem1 = nullptr;

		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

输出:

__cplusplus: 201703
testing operator new & operator delete...
in-class self-define operator new (using malloc) called.
MyMemory ctor called.
MyMemory dtor called.
in-class self-define operator delete (using free) called.
The following is new debug infomation: Pyramid
MyMemory ctor called.
MyMemory dtor called.
in-class self-define operator delete (using free) called.
------------------------------
The end.
5.4同时定义全局的和类内的operator newoperator delete会有什么效果???

如果在全局层次重载opeator newoperator delete那么标准库的版本会被隐藏。需要注意的是全局层次甚至不能用namespace来进行限定。

如果全局层次和类内都重载opeator new时,对于类类型对象的new会使用类内版本,对于其它对象会使用全局层次重载的operator new
当对类类型对象使用全局作用域符::时得到的是全局层次的operator new,不是标准库版本的。

如果全局层次和类内都重载operator delete时,对于类类型对象的delete会使用类内版本,对于其它对象会使用全局层次的operator delete
即使对类类型对象使用全局作用域符::时得到的仍然是全局版本的,而不是标准库版本的。

示例1,既实现全局版本又实现类内版本的operator newoperator delete,从运行结果可以看出全局版本会隐藏标准库版本:
test_global_operator_new.hpp

#pragma once

#include 
#include 
#include 

/*索引1,单个元素,抛出异常版本,开始****************************************************************/
void* operator new(size_t size){
    std::cout << "global, index 1, \"void* operator new(size_t size)\" called.\n";
    if(void* mem = malloc(size)) {
        return mem;
    }
    else {
        throw std::bad_alloc();
    }
}
void operator delete(void* ptr) noexcept {
    std::cout << "global, index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
    free(ptr);
}
/*单个元素,抛出异常版本,结束****************************************************************/

auto testGlobalOperatorNewDelete() -> int {
    std::cout << "testing global operator new & operator delete..." << std::endl;

    int *p1 = ::new int;
    // int *p1 = new int;  // 此句与上句一样都是调用重载版本的void* operator new(size_t size)
    ::delete p1;   // 从输出结果来看,此处是调用的是全局的版本,而不是标准库的版本
    p1 = nullptr;
    std::cout << "++++++++++\n";
    
    std::cout << "------------------------------" << std::endl;
    return 0;
}

test_operator_new.hpp

#pragma once

#include 
#include 
#include 
#include "test_global_operator_new.hpp"

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }

        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *p1 = new MyMemory();
        delete p1;
        p1 = nullptr;
        std::cout << "++++++++++\n";

        MyMemory *p2 = ::new MyMemory();
        ::delete p2;   // 由于包含了头文件#include "test_global_operator_new.hpp",因此此处调用的是全局版本的,而不是调用的标准库的版本
        p2 = nullptr;        
        
 		std::cout << "------------------------------" << std::endl;
        return 0;
    }
}

main.cpp

#include "test_operator_new.hpp"
#include "test_global_operator_new.hpp"

#include 
#include 

int main()
{
    std::cout << "__cplusplus: " << __cplusplus << std::endl;

    test_operator_new::main();
    std::cout << "================================================\n";
    testGlobalOperatorNewDelete();

    std::cout << "The end." << std::endl;
}

输出:

__cplusplus: 201703
testing operator new & operator delete...
index 1, "void* operator new(size_t size)" called.
MyMemory ctor called.
MyMemory dtor called.
index 1, "void operator delete(void* ptr) noexcept" called.
++++++++++
global, index 1, "void* operator new(size_t size)" called.
MyMemory ctor called.
MyMemory dtor called.
global, index 1, "void operator delete(void* ptr) noexcept" called.
------------------------------
================================================
testing global operator new & operator delete...
global, index 1, "void* operator new(size_t size)" called.
global, index 1, "void operator delete(void* ptr) noexcept" called.
++++++++++
------------------------------
The end.

示例2,仅实现类内版本的operator newoperator delete,从运行结果可以看出当使用全局作用域::时会调用标准库的版本:
test_operator_new.hpp

#pragma once

#include 
#include 
#include 
//#include "test_global_operator_new.hpp"

/*
    如果在全局层次重载opeator new和operator delete那么标准库的版本会被隐藏;
    如果全局层次和类内都重载opeator new和operator delete时,对于类类型对象的new和delete会使用类内版本,对于其它对象会使用全局层次重载的operator new和operator delete;
    当对类类型对象使用全局作用域符::时得到的是全局层次的operator new和operator delete,不是标准库版本的
*/

namespace test_operator_new {
    class MyMemory {
    public:
        MyMemory() {
            std::cout << "MyMemory ctor called." << std::endl;
        }

        virtual ~MyMemory(){
            std::cout << "MyMemory dtor called." << std::endl;
        }

        /*索引1,单个元素,抛出异常版本,开始****************************************************************/
        void* operator new(size_t size){
            std::cout << "index 1, \"void* operator new(size_t size)\" called.\n";
            if(void* mem = malloc(size)) {
                return mem;
            }
            else {
                throw std::bad_alloc();
            }
        }
        void operator delete(void* ptr) noexcept {
            std::cout << "index 1, \"void operator delete(void* ptr) noexcept\" called.\n";
            free(ptr);
        }
        /*单个元素,抛出异常版本,结束****************************************************************/
    };
    
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *p1 = new MyMemory();
        delete p1;
        p1 = nullptr;
        std::cout << "++++++++++\n";

        MyMemory *p2 = ::new MyMemory();   // 调用的标准库的版本
        ::delete p2;                       // 调用的标准库的版本
        p2 = nullptr;        
        
 		std::cout << "------------------------------" << std::endl;
        return 0;
    }
}

main.cpp

#include "test_operator_new.hpp"
//#include "test_global_operator_new.hpp"

#include 
#include 

int main()
{
    std::cout << "__cplusplus: " << __cplusplus << std::endl;

    test_operator_new::main();
    std::cout << "================================================\n";
  
    std::cout << "The end." << std::endl;
}

输出:

__cplusplus: 201703
testing operator new & operator delete...
index 1, "void* operator new(size_t size)" called.
MyMemory ctor called.
MyMemory dtor called.
index 1, "void operator delete(void* ptr) noexcept" called.
++++++++++
MyMemory ctor called.
MyMemory dtor called.
------------------------------
================================================
The end.
5.5定义全局的operator newoperator delete时切不可使用名称空间
#include 
#include 

namespace track_buffer
{
    auto operator new(std::size_t num_bytes) ->void*
    {
        return malloc(num_bytes);
    }
}

int main()
{
    int *p = new int();
}

以上代码会提示(Visual Studio 2017 Debug x64):

错误	C2323	“track_buffer::operator new”: 非成员运算符 new 或 delete 函数不可声明为静态的,也不可在全局命名空间之外的命名空间中进行声明
5.6显式地调用析构函数

既可以通过对象调用析构函数,也可以通过对象的指针或引用调用析构函数,这与调用其它成员函数没什么区别。
例如:

std::string  *sp = new std::string("value");
using namespace std;
sp->~string();

调用析构函数会销毁对象,但是不会释放内存,如果需要的话,可以重新使用该空间。

6.显式地删除/默认化operator newoperator delete

显式地删除或默认化不局限于构造函数和赋值运算符,也可以针对operator new
例如,下面的类删除了operator newoperator new[],也即是说该类不能通过newnew[]进行动态创建。

#include 
#include 

namespace test_operator_new {
	class MyMemory {
	public:
		MyMemory()=default;
		virtual ~MyMemory() {}
		
        void* operator new(size_t size) = delete;
        void* operator new[](size_t size) = delete;
	};
	
	
    auto main() -> int {
		std::cout << "testing operator new & operator delete..." << std::endl;

        MyMemory *p1 = new MyMemory;    // error: use of deleted function 'static void* test_operator_new::MyMemory::operator new(size_t)'
        MyMemory *p2 = new MyMemory[3]; // error: use of deleted function 'static void* test_operator_new::MyMemory::operator new [](size_t)'

		std::cout << "------------------------------" << std::endl;

        return 0;
    }
}

7. Reference

1.Marc Gregoire, Nicholas A. Solter, Scott J. Kleper. C++高级编程(第2版). 清华大学出版社,2012.(P593-P599)
2. Stanley B. Lippman, Josée Lajoie, Barbara E. Moo. C++ Primer 中文版(第5版).电子工业出版社,2013.(P726-P730)
3. Scott Meyers. Effective C++ 改善程序与设计的55个具体做法(第三版), 电子工业出版社,2011.(P240,P252)


文章较长,难免有错误、疏漏之处,请评论区留言交流。
如果觉得对你有用,欢迎一键五连:点赞、评论、收藏、打赏、分享。

你可能感兴趣的:(C++,c++,operator,new)