动态对象创建(二)重载new和delete
动态对象创建(二)重载new和delete
前言
上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载new和delete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家。
通常我们为了一些目的而使用new和delete的内存分配系统,但是在特殊情况下,它并不能够满足需要。最常见的改变分配系统的原因是出于效率考虑:也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。C++允许重载new和delete来实现我们自己的存储分配方案,所以可以用它来处理问题。
另一个问题就是堆碎片:分配不同大小的内存可能会在堆上产生很多碎片,以至于很快用完内存。虽然内存可能还有,但是由于都是碎片,也就找不到足够大的内存块满足需要。通过为特定类创建自己的内存分配器,可以确保这种情况不会发生。
当我们在重载operator new()和operator delete()时,我们只是改变了原有的内存分配方法。编译器将用重载的new代替默认版本去分配内存,然后为那个内存调用构造函数。所以,虽然当编译器看到new时,编译器分配内存并调用构造函数,但是当重载new时,可以改变的只是内存分配部分。
接下来,我分为三个部分详细讲解重载new和delete:重载全局new和delete、对于一个类重载new和delete以及为数组重载new和delete。
重载全局new和delete
当我们重载全局的new和delete的时候,可想而知,就会使默认版本完全不能被访问--甚至在这个重新定义里也不能调用它们。
重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。
我们首先需要明白的一点就是:operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。
#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 delete(void* m)
{
puts("operator delete");
free(m);
} class S
{
public:
S() {puts("S::S()"); }
~S() {puts("S::~S()"); }
private:
int an[];
}; int main()
{
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;
return ;
}
对于一个类重载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()
{
Framis* f[Framis::psize];
try {
for (int i=; i<Framis::psize; i++) {
f[i]=new Framis;
}
new Framis;
} catch(bad_alloc) {
cerr<< "Out of memory!" <<endl;
}
delete f[];
f[]=;
Framis* X=new Framis;
delete X;
for (int j=; j<Framis::psize; j++) {
delete f[j];
}
return ;
}
为数组重载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 ;
}
总结
1:对于C++中的动态对象创建又有了新的认识,学习了重载new和delete
2:C++空类的大小是1
3:最让我激动的就是:C++程序把运行结果写入新创建的文档里面,这个和ACM里常用的文件读写还是不一样滴。
好吧,继续努力!!!
动态对象创建(二)重载new和delete的更多相关文章
- C/C++对Lu系统内置动态对象进行运算符重载
欢迎访问Lu程序设计 C/C++对Lu系统内置动态对象进行运算符重载 1 说明 要演示本文的例子,你必须下载Lu32脚本系统.本文的例子需要lu32.dll.lu32.lib.C格式的头文件lu32. ...
- 动态创建二维vector数组 C和C++ 及指针与引用的区别
二维vectorvector<vector <int> > ivec(m ,vector<int>(n)); //m*n的二维vector 动态创建m*n的二 ...
- C/C++注册动态对象到Lu系统并进行运算符重载
欢迎访问Lu程序设计 C/C++注册动态对象到Lu系统并进行运算符重载 1 说明 要演示本文的例子,你必须下载Lu32脚本系统.本文的例子需要lu32.dll.lu32.lib.C格式的头文件lu32 ...
- 必须要注意的 C++ 动态内存资源管理(二)——指针对象简单实现
必须要注意的 C++动态内存资源管理(二)——指针对象简单实现 四.拷贝类型的资源 上节我们说过,对于图片类型的资源我们有时候往往采用拷贝(如果对于那种公共图片,可能采用唯一副本,提供 ...
- 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间
[源码下载] 速战速决 (5) - PHP: 动态地创建属性和方法, 对象的复制, 对象的比较, 加载指定的文件, 自动加载类文件, 命名空间 作者:webabcd 介绍速战速决 之 PHP 动态地创 ...
- c++——对象的动态建立和释放(new 和delete)
3.8 对象的动态建立和释放 1 new和delete基本语法 1)在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除.在C语言中是利用库函数malloc和free来 ...
- C语言 动态创建二维数组
/*C语言 如何动态创建二维数组 转化为一维数组申请数组,创建和释放都比较简单 */ #include <stdlib.h> #include <stdio.h> #inclu ...
- 读书笔记jvm探秘之二: 对象创建
对象是面向对象设计语言无法回避的东西,可见其重要性,JAVA的对象相较于C++来说,不算很复杂,但是我们看到一句话背后往往有很多东西值得探讨(NEW关键字). 对象如何被创建? 首先一句简单的NEW语 ...
- Java虚拟机(二)-对象创建
这一篇大致说明一下,对象在Java堆中对象分配.内存布局以及访问定位 1.对象的创建 虚拟机在遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引 ...
随机推荐
- UBIFS 术语
B+ tree: base head: budgeting: 空闲空间评估 bud: 一个日志使用的eraseblock cnode: commit: 更新index到flash上的过程 commit ...
- Linux目录处理命令
1 创建命令 mkdir -p 目录名 其中 -p表示递归创建,英文为make directories td@td-Lenovo-IdeaPad-Y410P:~$ mkdir Test 上述命令在 ...
- react数组key的唯一性
1.不要使用数组的index索引作为key 2.在相邻的元素间,一定确保key的唯一性,如果出现了相同的 key,会抛出一个 Warning,告诉相邻组件间有重复的 key 值.并且只会渲染第一个重复 ...
- react热加载失败
react热加载失败 原因:路径名字大小写错误, 不是全部加载失败,有的时候可以用,有的时候不可以 热加载插件:webpack-dev-server
- paho-mqtt
mqtt 参考: https://pypi.org/project/paho-mqtt/ https://github.com/eclipse/paho.mqtt.python #服务端 [root@ ...
- python virtualenv virtualenvwrapper
python中的virtualenv模块能够将项目环境分隔开,而不是使用全局的环境,非常实用. 首先pip install virtualenv 如何创建一个环境virtualenv testvir ...
- 【重点突破】—— React实现富文本编辑器
前言:富文本编辑器Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器. 一.安装插件 react-draft-wysiwyg: 文本编辑器插件 dra ...
- Android使用TextView,设置onClick属性无效解决的方法
Android在布局文件里为View提供了onClick属性.用法例如以下: <TextView android:id="@+id/user" android:layout_ ...
- 构建Spring Boot程序有用的文章
构建Spring Boot程序有用的文章: http://www.jb51.net/article/111546.htm
- Phalcon 上下文编码(Contextual Escaping)
站点及其他B/S应用极易受到 XSS 攻击,虽然PHP提供了转义功能.在某些情况下依旧不够安全.在Phalcon中 Phalcon\Escaper 提供了上下文转义功能,这个模块是由C语言实现的, 这 ...