本文参考文献:GeekBand课堂内容,授课老师:侯捷html
:深度探索C++对象模型(侯捷译)数组
:网络资料: http://www.leavesite.com/geekband-cpp-5.html网络
http://blog.csdn.net/wudaijun/article/details/9273339函数
本周的课题是:“ 为上周题目中的 Fruit和Apple 添加 构造函数与 析构函数, 并在构造函数与析构函数中打印控制台信息,观察构造和析枸调用过程。而后为Apple类重载::operator new和 ::operator delete,在控制台打印信息,并观察调用结果。”测试
虽然其中构造与析构调用过程上次代码里已经实现了,而且比如今这份还要完善一些。但作为开场白再讲一遍比较好!ui
首先,先看下 类的结构。Apple 类继承自基类Fruitthis
1 //基类 2 class Fruit 3 { 4 public: 5 //使用自带的构造函数 6 Fruit() 7 { 8 cout << "Call Fruit Constructor.this = " <<this<< endl; 9 } 10 //打印变量内存地址 11 void print(){} 12 //虚函数的影响 13 virtual void process(){} 14 15 virtual ~Fruit() 16 { 17 cout << "Call Fruit Destructor = " << this << endl; 18 } 19 20 private: 21 int no; 22 double weight; 23 char key; 24 }; 25 26 //这里考虑本身自己的虚函数,及基类的虚函数 27 class Apple : public Fruit 28 { 29 public: 30 //使用默认的构造函数 31 Apple() 32 { 33 cout << "Call Apple Constructor.this = " << this << endl; 34 }; 35 //打印成员数据 36 void save(){} 37 virtual void process(){} 38 virtual ~Apple() 39 { 40 cout << "Call Apple Destructor.this = " << this << endl; 41 } 42 43 //测试2、抛出异常 44 static void* operator new(size_t size); 45 //测试3、没有抛出异常,此版本须要注释掉测试二 46 //static void* operator new(size_t size, const std::nothrow_t& nothrow_value); 47 48 //测试4、带有调试信息的版本,此版本须要注释掉测试2、测试三 49 //inline void* Apple::operator new(size_t size, const char* file, int line); 50 51 //delete 版本 52 static void operator delete(void* ptr, size_t size) throw(); 53 54 //测试5、测试数组 55 static void *operator new[](size_t size); 56 static void operator delete[](void *ptr); 57 58 private: 59 int size; 60 char type; 61 };
那么问题来了,Apple 类和Fruit谁先构造、又谁先析构呢?进而思考,基类和子类谁更大一些?spa
众所周知,子类拥有父类的一切信息,并且子类有些信息更具体,好比鸟都有翅膀,这是共性。可是好比啄木鸟的嘴特别长,这就是特性。天然界是共性与特性的统一。.net
不过从哲学的角度来看,如“人是社会关系的总和”,讲的也是这个道理。3d
扯得有点远了,看图!因此构造时先构造内部,而后构造外部,析构时正好相反!
能够充分证实这个观点,还有问题的话,拷贝我上篇blog代码,能够有更详细的分析,这里就不展开讲了。毕竟只是开场白!
一个类中,若是什么数据都没有!打印结果倒是1
class Empty { }; int main(int argc, char** argv) { std::cout << sizeof(Empty) << std::endl; return 0; }
因此咱们为类进行new 重载时应该也要考虑到这一点。至于为何是1,不是0,也而不是其余的数据。我没弄清楚。但根据调试结果来分析,
咱们在重载应该考虑到这一点。
首先应该判断下size是否为0。有指针时也要判断指针是否为空。
inline void* Apple::operator new(size_t size) { if (size == 0) { return malloc(1); } void *ptr = malloc(size); if (ptr) { cout << "Apple::size = " << size << " Apple::Address = " << ptr << endl; return (Apple*)ptr; } else { throw bad_alloc(); } }
由叶卡同窗的blog中记录的 C++ Primer 557所示,成员operator new() 和 operator delete()会自动成为static成员。
所以,它们没有this指针,并且也不会修改物件内容,仅仅做为开辟空间、和清楚空间的做用!
throwing (1) void* operator new (std::size_t size) throw (std::bad_alloc); nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw(); placement (3) void* operator new (std::size_t size, void* ptr) throw();
第一、2种的区别 是有无抛出异常,其中有抛出异常的还能够进一步抛出信息,下面将会分析。
第3种 placement new,它也是对operator new的一个重载,定义于<new>中,它多接收一个ptr参数,但它只是简单地返回ptr。这里暂时没有详细分析,请同窗自行查阅资料。(我上面的推荐资料里就有)
/* 测试2、抛出异常的版本 */ inline void* Apple::operator new(size_t size) { if (size == 0) { return malloc(1); } 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。与上述相对应。
/* 测试3、没有抛出异常的版本 */ 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 这类信息每每会用在调试代码阶段。能比较方便的显示出行数及文件信息。
* 测试4、抛出异常,并带有调试信息的版本 此版本使用时,会对以上两个版本发生冲突,须要注释掉另外两个函数,及使用 */ inline void* Apple::operator new(size_t size, const char* file, int line) { //即便是空类,大小也为1 if (size == 0) { return malloc(1); } 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(); } }
在测试头部也要添加信息
//测试4、打开注释 //#define new new(__FILE__, __LINE__)
如图所示,显示了文件、及行数信息,方便调试。
/* 测试1、栈空间,使用自带的new 和全局new */ Apple ptrApple; Fruit *ptr = new Fruit(); delete ptr; Apple* ptr1 = new Apple();//Apple 是临时变量,所占空间是以new动态分配而得,并由p指向,占用空间为堆 delete ptr1;
这里有两种方法使用Apple 类,第一种为栈调用的方法,第二种为堆调用的方法(本身malloc)。这两种方法调用new 和delete的位置不一样。
如图所示, 这里实际上有几个步骤:
一、分配内存.
二、指针类型转换
三、调用构造函数
分配内存这一操做就是由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 == 0) { return malloc(1); } 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[3]; cout << "ptr3[0] addr: " << ptr3 << endl; cout << "ptr3[1] addr: " << ptr3 + 1 << endl; cout << "ptr3[2] addr: " << ptr3 + 2 << endl; delete[] ptr3; ptr3 = NULL;
下面用图来解释下,(此图源于某blog内容,后面图保存了,却找不到来源,请做者勿怪,若有侵权,请联系我,谢谢)
delete的过程
烦请路过的朋友,批评指针。感谢网络的无私奉献者。 修改于 2016.08.15 17:28
内容修改中,8月15日晚11:30分前
上传最新版本