本文参考文献: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重载的实现及分析

      new:指我们在C++里通常用到的运算符,比如A* a = new A;  对于new来说,有new和::new之分,前者位于std
      operator new():指对new的重载形式,它是一个函数,并不是运算符。对于operator new来说,分为全局重载和类重载,全局重载是void* ::operator new(size_t size),在类中重载形式 void* A::operator new(size_t size)。还要注意的是这里的operator new()完成的操作一般只是分配内存,事实上系统默认的全局::operator new(size_t size)也只是调用malloc分配内存,并且返回一个void*指针。而构造函数的调用(如果需要)是在new运算符中完成的。

-----------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重载的实现及分析的更多相关文章

  1. [GeekBand] C++继承关系下虚函数内存分布

    本文参考文献:GeekBand课堂内容,授课老师:侯捷 :深度探索C++对象模型(侯捷译) :网络资料,如:http://blog.csdn.net/sanfengshou/article/detai ...

  2. 内存分配(new/delete,malloc/free,allocator,内存池)

    以下来源http://www.cnblogs.com/JCSU/articles/1051826.html 程序员们经常编写内存管理程序,往往提心吊胆.如果不想触雷,唯一的解决办法就是发现所有潜伏的地 ...

  3. [百度空间] [原] 全局operator delete重载到DLL

    由于很久没有搞内存管理了,很多细节都忘记了今天项目要用到operator delete重载到DLL,发现了问题,网上搜索以后,再对比以前写的代码,发现了问题:原来MSVC默认的operator new ...

  4. 【转】C++类-内存分布

    C++类内存分布 - 转载自Jerry19880126 - 博客园 的文章 在上面这篇文章的基础上做了些整理. 主要讨论了C++类对象的内存分布结构. 来看看编译器是怎么处理类成员内存分布的,特别是在 ...

  5. new和delete重载

    1. 简介 new/delete关键字,其本质是预定义的操作符,因此支持重载 默认new和delete的行为: new:    ①获取内存空间(默认为堆空间):②在获取的空间中调用构造函数创建对象 d ...

  6. C++ new和delete重载

    C++ new和delete重载 2012-02-15 23:25:33|  分类: C/C++|举报|字号 订阅           首先,new和delete是运算符,重载new和delete是可 ...

  7. 程序的内存分布 - 以 Linux 为例,基于 C 语言分析

    这里以 Linux 为例,用 C 语言进行演示. 内存模型 - 内存空间名称 内容 读写操作 分配时机 高地址 kernel 内核空间 命令行参数.环境变量等 不可读写 程序运行时 - stack 栈 ...

  8. C++类内存分布

    http://www.cnblogs.com/jerry19880126/p/3616999.html#undefined 书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看 ...

  9. 内存管理运算符new delete与内存管理函数malloc free的区别——已经他们对对象创建的过程。

    (1)内存管理函数与内存管理运算符的区别 内存管理函数有内存分配函数,malloc calloc realloc 以及内存释放函数free. 内存管理运算符有new 和delete. 两种内存管理方式 ...

随机推荐

  1. php网页跳转无法获取session值

    今日编写项目,需要在跳转后的页面获取session值进行自动登录操作,但是明明在传输页面可以打印出session值,但在接受页面却显示session值为空,经确认脚本中的session_start() ...

  2. Web应用开发(Servlet+html+Mysql)入门小示例

    在安装好eclipse并配置完安装环境.安装好tomcat的前提下: 1.新建Dynamic Web Project,选择好运行的tomcat服务器版本等:2.在WebContent下:   新建fo ...

  3. Java Web学习总结(6)——通过Servlet生成验证码图片

    一.BufferedImage类介绍 生成验证码图片主要用到了一个BufferedImage类,如下: 创建一个DrawImage Servlet,用来生成验证码图片 package gacl.res ...

  4. oracle-function 练习

    /* *scm_iss.test_imti_fun2 *带有输入參数的Function */ CREATE OR REPLACE FUNCTION TEST_IMTI_FUN2(P_NO IN NUM ...

  5. GO语言学习(二)Windows 平台下 LiteIDE 的安装和使用

    1. 安装 Go 语言并设置环境变量 参考GO语言学习(一) 2. MinGW 的下载和安装 Windows 下的 Go 调试还需要安装 MinGW. 2.1 下载安装工具的安装 最新版本下载安装工具 ...

  6. rz、sz (上传下载)命令参数的解释

    $ man rz,查看帮助: -a, –ascii -b, –binary 用binary的方式上传下载,不解释字符为 ascii -e, –escape 强制escape 所有控制字符,比如 Ctr ...

  7. how to query for a list<String> in jdbctemplate?--转载

    原文地址:http://stackoverflow.com/questions/13354158/how-to-query-for-a-liststring-in-jdbctemplate   I'm ...

  8. 2、JNI说明

    JNI (Java Native Interface) 1. JAVA调用CLinux是用C语言写的,可以写一个APP简单调用open,read,write来访问驱动程序;Android是用Java写 ...

  9. ZOJ 1136 Longest Ordered Subsequence DP

    传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1136 题目大意:给定一串序列,求最长的升序列长度,如1, 7, 3, ...

  10. Maven项目中mvn clean后找不到測试类问题

    在Maven项目中进行单元測试,但mvn clean后又一次mvn install项目,再次进行单元測试.会有下面的错误. <span style="font-family:KaiTi ...