重载new和delete运算符
内存管理运算符 new、new[]、delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数。一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内存时才会重载。
以成员函数的形式重载 new 运算符:
void * className::operator new( size_t size ){
//TODO:
}
以全局函数的形式重载 new 运算符:
void * operator new( size_t size ){
//TODO:
}
两种重载形式的返回值相同,都是void *
类型,并且都有一个参数,为size_t
类型。
在重载 new 或 new[] 时,无论是作为成员函数还是作为全局函数,它的第一个参数必须是 size_t 类型。size_t 表示的是要分配空间的大小,对于 new[] 的重载函数而言,size_t 则表示所需要分配的所有空间的总和。
重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。
我们首先需要明白的一点就是:operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。
size_t 在头文件 <cstdio> 中被定义为
typedef unsigned int size_t;
,也就是无符号整型。
当然,重载函数也可以有其他参数,但都必须有默认值,并且第一个参数的类型必须是 size_t。
同样的,delete 运算符也有两种重载形式。以类的成员函数的形式进行重载:
void className::operator delete( void *ptr){
//TODO:
}
以全局函数的形式进行重载:
void operator delete( void *ptr){
//TODO:
}
两种重载形式的返回值都是 void 类型,并且都必须有一个 void 类型的指针作为参数,该指针指向需要释放的内存空间。
当我们以类的成员函数的形式重载了new 和 delete 操作符,其使用方法如下:
C * c = new C; //分配内存空间
//TODO:
delete c; //释放内存空间
如果类中没有定义 new 和 delete 的重载函数,那么会自动调用内建的 new 和 delete 运算符。
重载全局new和delete
#include <QCoreApplication> #include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define inf 0x7fffffff
using namespace std; void* operator new(size_t sz)
{
printf("operator new: %d Bytes\n",sz);
void* m = malloc(sz);
if (!m) puts("out of memory");
return m;
} void* operator new [](size_t sz)
{
printf("operator new: %d Bytes\n",sz);
void* m = malloc(sz);
if (!m) puts("out of memory");
return m;
} void operator delete(void* m)
{
puts("operator delete");
free(m);
} void operator delete[](void* m)
{
puts("operator delete");
free(m);
} class S
{
public:
S() {puts("S::S()"); }
~S() {puts("S::~S()"); }
private:
int an[];
}; void func()
{ puts("creating & destroying an int");
int* q = new int();
delete q;
puts("creating & destroying an int[]");
int* p = new int[]();
delete []p;
puts("creating & destroying an s");
S* s = new S;
delete s;
puts("creating & destroying S[3]");
S* sa = new S[];
delete []sa;
} int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); func(); return a.exec();
}
输出:
creating & destroying an int
operator new: Bytes
operator delete
creating & destroying an int[]
operator new: Bytes
operator delete
creating & destroying an s
operator new: Bytes
S::S()
S::~S()
operator delete
creating & destroying S[]
operator new: Bytes
S::S()
S::S()
S::S()
S::~S()
S::~S()
S::~S()
operator delete
对于一个类重载new和delete
为一个类重载new和delete的时候,尽管不必显式的使用static,但是实际上仍是在创建static成员函数。它的语法也和重载任何其它运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的new和delete仍为所有其他类型对象使用(除非它们也有自己的new和delete)。这个和全局变量、局部变量的意思是一样的。
#include<iostream>
#include<cstddef>
#include<fstream>
#include<new>
using namespace std;
ofstream out("Framis.out"); class Framis
{
public:
enum{psize = };
Framis() {out<< "Framis()" <<endl; }
~Framis() {out<< "~Framis() ... " <<endl; }
void* operator new(size_t) throw (bad_alloc);
void operator delete(void*);
private:
enum{sz = };
char c[sz];
static unsigned char pool[];
static bool alloc_map[];
};
unsigned char Framis::pool[psize*sizeof(Framis)];
bool Framis::alloc_map[psize]={false}; void* Framis::operator new(size_t sz) throw(bad_alloc)
{
for (int i=; i<psize; ++i) {
if (!alloc_map[i]) {
out<< "using block " << i << " ... ";
alloc_map[i]=true;
return pool+(i*sizeof(Framis));
}
}
out<< "out of memory" <<endl;
throw bad_alloc();
} void Framis::operator delete(void* m)
{
if (!m) return;
unsigned long block = (unsigned long)m-(unsigned long)pool;
block /= sizeof(Framis);
out<< "freeing block " << block <<endl;
alloc_map[block]=false;
} int main()
{
cout << sizeof(Framis) << endl;
Framis* f[Framis::psize];
try {
for (int i=; i<Framis::psize; i++) {
f[i]=new Framis; // new 的时候先跑进 new 重载函数中,再调用构造函数
}
new Framis;
} catch(bad_alloc) {
cerr<< "Out of memory!" <<endl;
}
delete f[];
f[]=;
Framis* X=new Framis;
delete X;// delete 的时候先调用析构函数,再跑进delete重载函数中
for (int j=; j<Framis::psize; j++) {
delete f[j];
}
return ;
}
// sizeof Framis
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
using block ... Framis()
out of memory
Out of memory!
~Framis() ...
freeing block
using block ... Framis()
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
~Framis() ...
freeing block
输出
为数组重载new和delete
上一段文字中我们讲到如果为一个类重载operator new()和operator delete(),那么无论何时创建这个类的一个对象都将调用这些运算符。但是如果要创建这个类的一个对象数组的时候,全局operator new()就会被立即调用,用来为这个数组分配足够的内存。对此,我们可以通过为这个类重载运算符的数组版本,即operator new[]和operator delete[],来控制对象数组的内存分配。
#include<iostream>
#include<fstream>
#include<new>
using namespace std;
ofstream trace("ArrayOperatorNew.out"); class Widget
{
public:
Widget() {trace<< "*" <<endl; }
~Widget() {trace<< "~" <<endl; }
void* operator new(size_t sz) {
trace<< "Widget::new: " << sz << " byte" <<endl;
return ::new char[sz];
}
void operator delete(void* p) {
trace<< "Widget::delete" <<endl;
::delete []p;
}
void* operator new[](size_t sz) {
trace<< "Widget::new[]: " << sz << " bytes" <<endl;
return ::new char[sz];
}
void operator delete[](void* p) {
trace<< "Widget::delete[]" <<endl;
::delete []p;
}
private:
enum{sz= };
int an[sz];
}; int main()
{
trace<< "new Widget" <<endl;
Widget* w=new Widget;
trace<<endl<< "delete Widget" <<endl;
delete w;
trace<<endl<< "new Widget[25]" <<endl;
Widget* wa=new Widget[];
trace<<endl<< "delete []Widget" <<endl;
delete []wa;
return ;
}
new Widget
Widget::new: byte
* delete Widget
~
Widget::delete new Widget[]
Widget::new[]: bytes
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* delete []Widget
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
Widget::delete[]
输出
C++空类的大小是1
重载new和delete运算符的更多相关文章
- c/c++ 重载new,delete运算符 placement new
重载new,delete运算符 new,delete在c++中也被归为运算符,所以可以重载它们. new的行为: 先开辟内存空间 再调用类的构造函数 开辟内存空间的部分,可以被重载. delete的行 ...
- C++重载new和delete运算符
内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...
- C++学习32 重载new和delete运算符
内存管理运算符 new.new[].delete 和 delete[] 也可以进行重载,其重载形式既可以是类的成员函数,也可以是全局函数.一般情况下,内建的内存管理运算符就够用了,只有在需要自己管理内 ...
- 重载全局new/delete实现内存检测
下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator ...
- 重载new和delete
当我们创建一个new表达式时,会发生两件事.首先使用operator new()分配内存,然后调用构造函数.在delete表达式里,调用了析构函数,然后使用operator delete()释放内存. ...
- C/C++基础----特殊工具和技术 (重载new和delete,RTT,限定作用域的枚举类型,类成员指针,嵌套类,局部类,volatile,链接指示 extern “C”)
重载new和delete 1调用operator new( 或new[])标准库函数分配足够大的.原始的.未命名的内存空间以便存储特定类型的对象 2编译器运行相应地构造函数以构造这些对象,并为其传入初 ...
- 动态对象创建(二)重载new和delete
动态对象创建(二)重载new和delete 前言 上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载new和delete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家. 通常 ...
- [C++] 重载new和delete——控制内存分配
1.new和delete表达式的工作机理 1)new表达式实际执行了三步 string *sp=new string("aaaa"); ];//string采用默认初 ...
- C++学习28 重载>>和<<(输入输出运算符)
在C++中,系统已经对左移运算符“<<”和右移运算符“>>”分别进行了重载,使其能够用于输入输出,但是输入输出的处理对象只能是系统内建的数据类型.系统重载这两个运算符是以系统类 ...
随机推荐
- LINUX驱动笔记 目录
笔记参考了宋宝华老师的<Linux设备驱动开发详解:基于最新的Linux 4.0内核>以及韦东山老师的嵌入式驱动教程 笔记开发环境: 单板:第一章到第八章使用TINY4412-1611:第 ...
- 多线程面试题之【三线程按顺序交替打印ABC的方法】
建立三个线程,线程名字分别为:A.B.C,要求三个线程分别打印自己的线程名字,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印.打印10轮,打印完毕控制台输出字符串:&q ...
- 谷歌浏览器调用activex控件方法
原文转自 https://jingyan.baidu.com/article/af9f5a2d0ebe5543140a4596.html activex是由微软开发,所以在支持上,目前原生态支持的只有 ...
- Go 编译 && 工具
编译和工具链 Go 的工具链非常丰富,从获取源码.编译.文档.测试.性能分析,到源码格式化.源码提示.重构工具等应有尽有 在 Go 中可以使用测试框架编写单元测试,使用统一的命令行即可测试及输出测试报 ...
- Nginx快速自查手册
本项目是一个 Nginx 极简教程,目的在于帮助新手快速入门 Nginx. demos 目录中的示例模拟了工作中的一些常用实战场景,并且都可以通过脚本一键式启动,让您可以快速看到演示效果. 概述 什么 ...
- 【转载】C#指定文件夹下面的所有内容复制到目标文件夹下面
在涉及到文件夹操作的过程中,有时候需要将文件夹下的所有内容复制拷贝到另一个文件夹,在C#的开发中有时候会遇到这个功能需求将指定文件夹下所有的内容复制到另一个文件夹,这个过程需要遍历所有的文件和目录.此 ...
- 在oracle中存入date类型数据遇到的问题及其解决方法(利用java.sql.date和Timestamp)
转自:https://blog.csdn.net/ShadowerWArden/article/details/80652377 1. 使用JDBC操作Oracle数据库时,使用java.sql.Da ...
- bootstrap table分页limit计算pageIndex和pageSize
由于bootstrap table的js无法直接获取pageSize和pageIndex的值,只能通过limit进行计算.
- 面试常考HTTP协议知识点
协议简介 1. 应用层协议, 一般以TCP为基础,数据收发通过TCP实现: 2. 一次性连接.服务器与客户端的每次连接只处理一个请求,下次请求重新建立连接: 3. 无状态协议.服务器不保留与客户交易时 ...
- ceph集群部署(基于jewel版)
环境 两个节点:ceph1.ceph2 ceph1: mon.mds.osd.0.osd.1 ceph2: osd.2.osd.3 网络配置: ceph1: 管理网络,eth0,10.0.0.20 存 ...