当前位置:网站首页>四万字长文说operator new & operator delete
四万字长文说operator new & operator delete
2022-07-05 19:23:00 【GarryLau】
1.new
和delete
的工作原理
C++允许重定义程序中内存分配和释放的方式,既可以在全局层次也可以在类层次。当需要编写一个内存池分配器的时候可以使用此技术。在类层次对operator new
和operator delete
进行重载是有价值的,但还是别在全局层次炫技了吧。
new
表达式与operator new
函数
标准库函数operator new
和operator delete
的名字容易让人误解。和其它operator
函数不同(比如operator=
),着两个函数并没有重载new
表达式或delete
表达式。
实际上,我们根本无法自定义new
表达式或delete
表达式的行为。
可以重载operator new
和operator delete
来控制内存的分配和释放,但不能重载new
表达式和delete
表达式。也就是说,可以自定义的是实际的内存分配和释放,但不能自定义构造函数和析构函数的调用。
我们提供新的operator new
函数和operator delete
函数的目的在于改变内存分配的方式,但是不管怎样,都不能改变new
运算符和delete
运算符的基本含义。
一条
new
表达式的执行过程总是分为三步:
new
表达式调用一个名为operator new
(或operator new[]
)的标准库函数,该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组);- 编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
- 对象被分配了空间并构造完成,返回一个指向该对象的指针。
// new表达式 std::string *sp = new std::string("a value"); // 分配并初始化一个std::string对象 std::string *arr = new std::string[10]; // 分配10个默认初始化的std::string对象
一条
delete
表达式执行过程总是分为两步:
对所指对象或者对象数组执行对应的析构函数;
编译器调用名为operator delete
或operator delete[]
的标准库函数释放内存空间。// delete表达式 delete sp; // 销毁*sp,然后释放sp指向的内存空间 delete [] arr; // 销毁数组中的元素,然后释放对应的内存空间
2.标准库定义的operator new
和operator delete
标准库定义了6种不同形式的new
表达式,每种形式都有对应的operator new
。
只有2种形式的delete
表达式可以调用:delete
和delete[]
,没有nothrow
和placement
的形式。然而有6种形式的operator delete
。
不同编译器(工具链)对operator new
和operator delete
实现的个数和形式是不一样的,因此实际使用时请查看对于特定编译器(工具链)的具体实现。
以Visual Studio 2017来展示标准库里对operator new
和operator delete
的定义,图1是版本信息:
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
// <http://www.gnu.org/licenses/>.
/** @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 <bits/c++config.h>
#include <exception>
#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 new
和operator delete
时也会出现类似构造函数的隐藏效应
当自定义实现重载抛异常版本void* operator new(size_t size)
或不抛异常版本void* operator new(size_t size, const std::nothrow_t&) noexcept
的其中一个时,编译器都会隐藏标准库的版本,即编译器会使用自定义的版本替换标准库的版本。这与类中自己编写了构造函数时会隐藏默认构造函数类似。假设对于上述两种原型重载了其中一种形式但实际使用的是另一种形式,编译器会报错。因此重载哪个版本只能使用哪个版本。
对于单个元素的版本之间的影响和数组的版本之间的影响都是如此,但单个元素版本与数组版本之间无此影响。详见下面小章节的示例。由于运行时对待operator new
和operator delete
时不太一样的,重载的operator new
基本都可以被主动调用,但重载的operator delete
正常情况下仅能调用原型是void operator delete(void* ptr) noexcept
的版本,章节4会进一步介绍。
3.1 单个元素,重载抛异常版本,不重载不抛异常版本,但企图使用不抛异常版本
注意,本篇文章仅讨论自定义版本的operator new
和operator delete
如何使用,不讨论operator new
和operator delete
函数的实现细节。因此本篇文章的代码示例并未遵守一些约定,比如operator new
都应该内含一个循环,反复调用某个new handler
。详见参考文献3的条款49和条款51
#include <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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) noexcept
的operator delete
那其它版本的对operator delete
的重载什么时候调用呢?详见4.3小章节吧。
4.1 void operator delete(void* ptr) noexcept
一定是个宠儿
单个元素版本示例:
#include <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 <new>
#include <cstdlib>
#include <iostream>
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 new(size_t size, const std::nothrow_t&) noexcept
的版本是摆设?
void* operator new(size_t size, const std::nothrow_t&) noexcept
的版本不是摆设,只有在构造函数抛出了异常的时候才会用到。
参考文献《C++高级编程(第2版)》P594说,4种(分别是参数中有std::nothrow_t
和没有的单个元素版本、数组版本)nothrow
和placement
形式的operator delete
只有在构造函数抛出异常时才会使用。在这种情况下,匹配调用构造函数之前分配内存时使用的operator new
的operator delete
会被调用。然而,正常删除一个指针,delete
会调用operator delete
或operator delete[]
(绝对不会调用nothrow
或placement
的形式)。
4.3.1 单个元素版本示例
#include <new>
#include <cstdlib>
#include <iostream>
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 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 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 <new>
#include <cstdlib>
#include <iostream>
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 new
和operator delete
可以在全局作用域中定义operator new
函数和operator delete
函数(包含申请单个元素的版本和数组的版本),也可以将它们定义为成员函数。
当将上述运算符函数定义成类的成员时,它们是隐式静态的,无须显示声明static
(这么做也不会引发错误)。因为operator new
用在对象构造之前而operator delete
用在对象销毁之后,所以这两个成员(new
和delete
)必须时静态的,而且它们不能操纵类的任何数据成员。
如果被分配(释放)的对象是类类型
(a)编译器首先在类及其基类的作用域中查找,如果该类含有operator new
成员或operator delete
成员,则相应的表达式将调用这些成员。否则,(b)编译器在全局作用域查找匹配的函数,如果编译器找到了用户自定义的版本,则使用该版本执行new
表达式或delete
表达式。(c )如果没有找到,则使用标准库定义的版本。
务必注意,Bjarne Stroustrup说“替换全局的operator new
和operator delete
时需要胆量的”。然而如果执迷不悟,一定要哦注意在operator new
的实现代码中不要对new
进行热河调用,否则会产生无限循环。
5.1标准库可被重载的8个函数的重载示例
#include <iostream>
#include <string>
#include <new>
#include <stdio.h>
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 <typeinfo>
#include <iostream>
#include <string>
#include <new>
#include <stdio.h>
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 new
和operator delete
定义带有额外参数的operator new
时,还应该定义对应的带有额外参数的operator delete
。但不能自己调用这个带有额外参数的operator delete
,只有在使用了带有额外参数的operator new
且对象的构造函数抛出了异常的时候才会调用这个operator delete
。
#include <iostream>
#include <string>
#include <new>
#include <cstdlib>
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 new
和operator delete
会有什么效果???
如果在全局层次重载opeator new
和operator delete
那么标准库的版本会被隐藏。需要注意的是全局层次甚至不能用namespace
来进行限定。
如果全局层次和类内都重载opeator new
时,对于类类型对象的new
会使用类内版本,对于其它对象会使用全局层次重载的operator new
;
当对类类型对象使用全局作用域符::
时得到的是全局层次的operator new
,不是标准库版本的。
如果全局层次和类内都重载operator delete
时,对于类类型对象的delete
会使用类内版本,对于其它对象会使用标准库的operator delete
;
当对类类型对象使用全局作用域符::
时得到的是标准库版本的。
示例:test_global_operator_new.hpp
#pragma once
#include <new>
#include <cstdlib>
#include <iostream>
/*索引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 <new>
#include <cstdlib>
#include <iostream>
#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; // 从输出结果来看,此处是调用的标准库的版本
p2 = nullptr;
std::cout << "------------------------------" << std::endl;
return 0;
}
}
main.cpp
#include "test_operator_new.hpp"
#include "test_global_operator_new.hpp"
#include <string>
#include <iostream>
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.
------------------------------
================================================
testing global operator new & operator delete...
global, index 1, "void* operator new(size_t size)" called.
++++++++++
------------------------------
The end.
5.5显式地调用析构函数
既可以通过对象调用析构函数,也可以通过对象的指针或引用调用析构函数,这与调用其它成员函数没什么区别。
例如:
std::string *sp = new std::string("value");
using namespace std;
sp->~string();
调用析构函数会销毁对象,但是不会释放内存,如果需要的话,可以重新使用该空间。
6.显式地删除/默认化operator new
和operator delete
显式地删除或默认化不局限于构造函数和赋值运算符,也可以针对operator new
。
例如,下面的类删除了operator new
和operator new[]
,也即是说该类不能通过new
或new[]
进行动态创建。
#include <iostream>
#include <new>
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)
文章较长,难免有错误、疏漏之处,请评论区留言交流。
如果觉得对你有用,欢迎一键五连:点赞、评论、收藏、打赏、分享。
边栏推荐
- Which securities company is better and which platform is safer for mobile account opening
- Fuzor 2020软件安装包下载及安装教程
- #夏日挑战赛#数据库学霸笔记,考试/面试快速复习~
- The relationship between temperature measurement and imaging accuracy of ifd-x micro infrared imager (module)
- 手机开户选择哪家券商公司比较好哪家平台更安全
- Explain in detail the functions and underlying implementation logic of the groups sets statement in SQL
- 【硬核干货】数据分析哪家强?选Pandas还是选SQL
- UWB ultra wideband positioning technology, real-time centimeter level high-precision positioning application, ultra wideband transmission technology
- 机器学习基础(三)——KNN/朴素贝叶斯/交叉验证/网格搜索
- Decision tree and random forest
猜你喜欢
Reinforcement learning - learning notes 4 | actor critical
That's awesome. It's enough to read this article
软件测试工程师是做什么的?待遇前景怎么样?
Go语言 | 01 WSL+VSCode环境搭建避坑指南
Android面试,android音视频开发
使用easyexcel模板导出的两个坑(Map空数据列错乱和不支持嵌套对象)
5 years of experience, 27 days of Android programmer interview, 2022 programmer advanced classic
Common - Hero Minesweeper
How to choose the notion productivity tools? Comparison and evaluation of notion, flowus and WOLAI
Teach you to deal with JS reverse picture camouflage hand in hand
随机推荐
Mariadb root用户及普通用户的密码 重置
基于FPGA的超声波测距
不愧是大佬,字节大牛耗时八个月又一力作
Go语言 | 02 for循环及常用函数的使用
打新债在哪里操作开户是更安全可靠的呢
软件测试是干什么的?学习有啥要求?
Android interview, Android audio and video development
R language Visual scatter plot graph, add labels to some data points in the graph, and always display all labels, even if they have too much overlap. Ggrep package helps
Information / data
How MySQL queries and modifies JSON data
[today in history] July 5: the mother of Google was born; Two Turing Award pioneers born on the same day
CF: B. almost Ternary Matrix [symétrie + règles de recherche + Construction + I am Construction Waste]
PG基础篇--逻辑结构管理(用户及权限管理)
华为让出的高端市场,小米12S靠徕卡能抢到吗?
word如何转换成pdf?word转pdf简单的方法分享!
JS解力扣每日一题(十二)——556. 下一个更大元素 III(2022-7-3)
开源 SPL 消灭数以万计的数据库中间表
块编辑器如何选择?印象笔记 Verse、Notion、FlowUs
Explain in detail the functions and underlying implementation logic of the groups sets statement in SQL
Get wechat avatar and nickname with uniapp