[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. 两种内存管理方式 ...
随机推荐
- Django模板变量,过滤器和静态文件引用
模版路径查找 首先去settings.py里面找TEMPLATES ,在TEMPLATES下面找DIRS,找到就返回,没找到就继续往下,如果APP_DIRS设置为为Ture,那么就会到上面 INSTA ...
- 洛谷 P3131 [USACO16JAN]子共七Subsequences Summing to Sevens
P3131 [USACO16JAN]子共七Subsequences Summing to Sevens 题目描述 Farmer John's NN cows are standing in a row ...
- iOS_06_基本运算符
一.算术运算 c语言一共有34种运算符,包括了常见的加减乘除 1.加法运算+ # 除了能做加法运算,还能表示正号:+5.+90 2.减法运算- # 除了能做减法运算,还能表示符号:-10.-200 3 ...
- DOM相关知识总结
DOM相关: 1.获取DOM元素 document.getElementById document.getElementsByName document.getElementsByTagName do ...
- centos php 安装memcached 扩展 支持sasl
1.安装sasl yum install cyrus-sasl-lib.x86_64 yum install cyrus-sasl-devel.x86_64 2.下载libmemcached wget ...
- ubuntu, 修改hosts文件
介绍下ubuntu下修改host实现域名指向本地的方法. Ubuntu系统的Hosts只需修改/etc/hosts文件,在目录中还有一个hosts.conf文件,刚开始还以为只需要修改这个就可以了,结 ...
- stm32单片机时钟中断的配置
原作者:http://www.eeworld.com.cn/mcu/article_2016082828940.html 配置流程: 1:系统时钟初始化,包括系统时钟和要开放的IO口或者功能的时钟 ...
- The behavior of App killed or restored by Android System or by users
What's the behavior of App killed or restored by Android System or by users? First, user kills the a ...
- 代码从windows下visual studio到andriod平台迁移的修改记录
前言 前言也是迁言,从windows的visual studio 2012平台迁移到android平台上,需用修改挺多的代码和需用注意地方. 我们当然的平台当初就考虑了其他平台跨平台的应用问题,所以一 ...
- [Redis专辑][1]ubuntu12.04下安装php-redis的方法和步骤
首次公布路径:phpredis的安装 非常久非常久没有写博文了,好多博文都没有整理完成,今天才抽时间整理完这一篇博文,希望能对大家有一定的帮助 首先对redis做个简单的介绍: Redis 是全然开源 ...