[GeekBand] C++ 内存分布—— new和delete重载的实现及分析
本文参考文献:GeekBand课堂内容,授课老师:侯捷
:深度探索C++对象模型(侯捷译)
:网络资料: http://www.leavesite.com/geekband-cpp-5.html
http://blog.csdn.net/wudaijun/article/details/9273339
本周的课题是:“ 为上周题目中的 Fruit和Apple 添加 构造函数与 析构函数, 并在构造函数与析构函数中打印控制台信息,观察构造和析枸调用过程。然后为Apple类重载::operator new和 ::operator delete,在控制台打印信息,并观察调用结果。”
虽然其中构造与析构调用过程上次代码里已经实现了,并且比现在这份还要完善一些。但做为开场白再讲一遍比较好!
首先,先看下 类的结构。Apple 类继承自基类Fruit
//基类
class Fruit
{
public:
//使用自带的构造函数
Fruit()
{
cout << "Call Fruit Constructor.this = " <<this<< endl;
}
//打印变量内存地址
void print(){}
//虚函数的影响
virtual void process(){} virtual ~Fruit()
{
cout << "Call Fruit Destructor = " << this << endl;
} private:
int no;
double weight;
char key;
}; //这里考虑自己本身的虚函数,及基类的虚函数
class Apple : public Fruit
{
public:
//使用默认的构造函数
Apple()
{
cout << "Call Apple Constructor.this = " << this << endl;
};
//打印成员数据
void save(){}
virtual void process(){}
virtual ~Apple()
{
cout << "Call Apple Destructor.this = " << this << endl;
} //测试二、抛出异常
static void* operator new(size_t size);
//测试三、没有抛出异常,此版本需要注释掉测试二
//static void* operator new(size_t size, const std::nothrow_t& nothrow_value); //测试四、带有调试信息的版本,此版本需要注释掉测试二、测试三
//inline void* Apple::operator new(size_t size, const char* file, int line); //delete 版本
static void operator delete(void* ptr, size_t size) throw(); //测试五、测试数组
static void *operator new[](size_t size);
static void operator delete[](void *ptr); private:
int size;
char type;
};
那么问题来了,Apple 类和Fruit谁先构造、又谁先析构呢?进而思考,基类和子类谁更大一些?
众所周知,子类拥有父类的一切信息,而且子类有些信息更具体,比如鸟都有翅膀,这是共性。但是比如啄木鸟的嘴特别长,这就是特性。自然界是共性与特性的统一。
不过从哲学的角度来看,如“人是社会关系的总和”,讲的也是这个道理。
扯得有点远了,看图!所以构造时先构造内部,然后构造外部,析构时正好相反!
可以充分证明这个观点,还有问题的话,拷贝我上篇blog代码,可以有更详细的分析,这里就不展开讲了。毕竟只是开场白!
一、new和delete重载的实现及分析
-----------wudaijun blog
1、重载时,一个类为空怎么处理?
一个类中,如果什么数据都没有!打印结果却是1
class Empty
{
}; int main(int argc, char** argv)
{
std::cout << sizeof(Empty) << std::endl;
return ;
}
所以我们为类进行new 重载时应该也要考虑到这一点。至于为什么是1,不是0,也而不是其他的数据。我没弄清楚。但根据调试结果来分析,
我们在重载应该考虑到这一点。
首先应该判断下size是否为0。有指针时也要判断指针是否为空。
inline void* Apple::operator new(size_t size)
{
if (size == )
{
return malloc();
}
void *ptr = malloc(size);
if (ptr)
{
cout << "Apple::size = " << size << " Apple::Address = " << ptr << endl;
return (Apple*)ptr;
} else
{
throw bad_alloc();
}
}
2、operator new() 和 operator delete() 会自动转换为static 成员
由叶卡同学的blog中记录的 C++ Primer 557所示,成员operator new() 和 operator delete()会自动成为static成员。
因此,它们没有this指针,而且也不会修改物件内容,仅仅作为开辟空间、和清楚空间的作用!
3、operator new() 的三种形式:
throwing ()
void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow ()
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement ()
void* operator new (std::size_t size, void* ptr) throw();
第1、2种的区别 是有无抛出异常,其中有抛出异常的还可以进一步抛出信息,下面将会分析。
第3种 placement new,它也是对operator new的一个重载,定义于<new>中,它多接收一个ptr参数,但它只是简单地返回ptr。这里暂时没有详细分析,请同学自行查阅资料。(我上面的推荐资料里就有)
/*
测试二、抛出异常的版本
*/ inline void* Apple::operator new(size_t size)
{
if (size == )
{
return malloc();
}
void *ptr = malloc(size);
if (ptr)
{
cout << "Apple::size = " << size << " Apple::Address = " << ptr << endl;
return (Apple*)ptr;
} else
{
throw bad_alloc();
}
}
运行图如下:
从上图分析得出,Fruit的Size为32,Apple 的Size为40。与上述相对应。
/*
测试三、没有抛出异常的版本
*/ inline void* Apple:: operator new(size_t size, const std::nothrow_t& nothrow_value)
{
//即使是空类,大小也为1
if (size == 0)
{
return malloc(1);
}
else
std::cout << "call Apple::operator new nothrow" << std::endl;
return malloc(size);
}
这个版本是没有返回异常信息的版本
如图所示,New的过程中那些打印信息并没有显示。
new 这类信息往往会用在调试代码阶段。能比较方便的显示出行数及文件信息。
*
测试四、抛出异常,并带有调试信息的版本
此版本使用时,会对以上两个版本发生冲突,需要注释掉另外两个函数,及使用
*/ inline void* Apple::operator new(size_t size, const char* file, int line)
{
//即使是空类,大小也为1
if (size == )
{
return malloc();
}
void *ptr = malloc(size);
if (ptr)
{
std::cout << "call A::operator new on file:" << file << " line:" << line << std::endl;
cout << "Apple::size = " << size << " Apple::Address = " << ptr << endl;
return (Apple*)ptr;
} else {
throw bad_alloc();
}
}
在测试头部也要添加信息
//测试四、打开注释
//#define new new(__FILE__, __LINE__)
如图所示,显示了文件、及行数信息,方便调试。
4、何时重载类中、全局的 operator new()
/*
测试一、栈空间,使用自带的new 和全局new
*/ Apple ptrApple; Fruit *ptr = new Fruit();
delete ptr;
Apple* ptr1 = new Apple();//Apple 是临时变量,所占空间是以new动态分配而得,并由p指向,占用空间为堆
delete ptr1;
这里有两种方法使用Apple 类,第一种为栈调用的方法,第二种为堆调用的方法(自己malloc)。这两种方法调用new 和delete的位置不同。
如图所示, 这里实际上有几个步骤:
1、分配内存.
2、指针类型转换
3、调用构造函数
分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),
通过以上结果对比,作用域覆盖原则,即在里向外寻找operator new的重载时,只要找到operator new()函数就不再向外查找,如果参数符合则通过,如果参数不符合则报错,而不管全局是否还有相匹配的函数原型。
既先查找类中的operator new()和 operator delete(),然后再执行全局operator new()和 operator delete()。
5、多维数组的重载
/*
测试五、类中重载new[] 和 delete[]
*/
inline void* Apple::operator new[](size_t size)
{
//即使是空类,大小也为1
if (size == )
{
return malloc();
}
cout << "This is Apple New[]! Now allocating space :" << size << "Byte!" << endl;
return malloc(size);
} inline void Apple::operator delete[](void *ptr)
{
if (ptr)
{
cout << "This is Apple Delete[], Now free space!" << endl;
free(ptr);
}
else
{
ptr = NULL;
}
}
Apple *ptr3 = new Apple[];
cout << "ptr3[0] addr: " << ptr3 << endl;
cout << "ptr3[1] addr: " << ptr3 + << endl;
cout << "ptr3[2] addr: " << ptr3 + << endl;
delete[] ptr3;
ptr3 = NULL;
下面用图来解释下,(此图源于某blog内容,后面图保存了,却找不到来源,请作者勿怪,如有侵权,请联系我,谢谢)
delete的过程
烦请路过的朋友,批评指针。感谢网络的无私奉献者。 修改于 2016.08.15 17:28
内容修改中,8月15日晚11:30分前
上传最新版本
[GeekBand] C++ 内存分布—— new和delete重载的实现及分析的更多相关文章
- [GeekBand] C++继承关系下虚函数内存分布
本文参考文献:GeekBand课堂内容,授课老师:侯捷 :深度探索C++对象模型(侯捷译) :网络资料,如:http://blog.csdn.net/sanfengshou/article/detai ...
- 内存分配(new/delete,malloc/free,allocator,内存池)
以下来源http://www.cnblogs.com/JCSU/articles/1051826.html 程序员们经常编写内存管理程序,往往提心吊胆.如果不想触雷,唯一的解决办法就是发现所有潜伏的地 ...
- [百度空间] [原] 全局operator delete重载到DLL
由于很久没有搞内存管理了,很多细节都忘记了今天项目要用到operator delete重载到DLL,发现了问题,网上搜索以后,再对比以前写的代码,发现了问题:原来MSVC默认的operator new ...
- 【转】C++类-内存分布
C++类内存分布 - 转载自Jerry19880126 - 博客园 的文章 在上面这篇文章的基础上做了些整理. 主要讨论了C++类对象的内存分布结构. 来看看编译器是怎么处理类成员内存分布的,特别是在 ...
- new和delete重载
1. 简介 new/delete关键字,其本质是预定义的操作符,因此支持重载 默认new和delete的行为: new: ①获取内存空间(默认为堆空间):②在获取的空间中调用构造函数创建对象 d ...
- C++ new和delete重载
C++ new和delete重载 2012-02-15 23:25:33| 分类: C/C++|举报|字号 订阅 首先,new和delete是运算符,重载new和delete是可 ...
- 程序的内存分布 - 以 Linux 为例,基于 C 语言分析
这里以 Linux 为例,用 C 语言进行演示. 内存模型 - 内存空间名称 内容 读写操作 分配时机 高地址 kernel 内核空间 命令行参数.环境变量等 不可读写 程序运行时 - stack 栈 ...
- C++类内存分布
http://www.cnblogs.com/jerry19880126/p/3616999.html#undefined 书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看 ...
- 内存管理运算符new delete与内存管理函数malloc free的区别——已经他们对对象创建的过程。
(1)内存管理函数与内存管理运算符的区别 内存管理函数有内存分配函数,malloc calloc realloc 以及内存释放函数free. 内存管理运算符有new 和delete. 两种内存管理方式 ...
随机推荐
- php网页跳转无法获取session值
今日编写项目,需要在跳转后的页面获取session值进行自动登录操作,但是明明在传输页面可以打印出session值,但在接受页面却显示session值为空,经确认脚本中的session_start() ...
- Web应用开发(Servlet+html+Mysql)入门小示例
在安装好eclipse并配置完安装环境.安装好tomcat的前提下: 1.新建Dynamic Web Project,选择好运行的tomcat服务器版本等:2.在WebContent下: 新建fo ...
- Java Web学习总结(6)——通过Servlet生成验证码图片
一.BufferedImage类介绍 生成验证码图片主要用到了一个BufferedImage类,如下: 创建一个DrawImage Servlet,用来生成验证码图片 package gacl.res ...
- oracle-function 练习
/* *scm_iss.test_imti_fun2 *带有输入參数的Function */ CREATE OR REPLACE FUNCTION TEST_IMTI_FUN2(P_NO IN NUM ...
- GO语言学习(二)Windows 平台下 LiteIDE 的安装和使用
1. 安装 Go 语言并设置环境变量 参考GO语言学习(一) 2. MinGW 的下载和安装 Windows 下的 Go 调试还需要安装 MinGW. 2.1 下载安装工具的安装 最新版本下载安装工具 ...
- rz、sz (上传下载)命令参数的解释
$ man rz,查看帮助: -a, –ascii -b, –binary 用binary的方式上传下载,不解释字符为 ascii -e, –escape 强制escape 所有控制字符,比如 Ctr ...
- how to query for a list<String> in jdbctemplate?--转载
原文地址:http://stackoverflow.com/questions/13354158/how-to-query-for-a-liststring-in-jdbctemplate I'm ...
- 2、JNI说明
JNI (Java Native Interface) 1. JAVA调用CLinux是用C语言写的,可以写一个APP简单调用open,read,write来访问驱动程序;Android是用Java写 ...
- ZOJ 1136 Longest Ordered Subsequence DP
传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1136 题目大意:给定一串序列,求最长的升序列长度,如1, 7, 3, ...
- Maven项目中mvn clean后找不到測试类问题
在Maven项目中进行单元測试,但mvn clean后又一次mvn install项目,再次进行单元測试.会有下面的错误. <span style="font-family:KaiTi ...