C++中对于内存的相关知识是一个比较重要而且复杂的知识点,我们需要不断的深入的对其进行研究。今天在这里我们将会针对C++堆对象的一些应用方法做一个详细的介绍,应该可以帮助大家对此有一个深刻的认识。#t#
产生C++堆对象的方法是使用new操作,如果我们禁止使用new不就行了么。再进一步,new操作执行时会调用operator new,而operator new是可以重载的。方法有了,就是使new operator 为private,为了对称,最好将operator delete也重载为private。
现在,你也许又有疑问了,难道创建栈对象不需要调用new吗?是的,不需要,因为创建栈对象不需要搜索内存,而是直接调整堆栈指针,将对象压栈,而operator new的主要任务是搜索合适的堆内存,为C++堆对象分配空间,这在上面已经提到过了。好,让我们看看下面的示例代码:
- #include stdlib.h 需要用到C式内存分配函数
- class Resource ; 代表需要被封装的资源类
- class NoHashObject
- {
- private
- Resource ptr ;指向被封装的资源
- ... ... 其它数据成员
- void operator new(size_t size) 非严格实现,仅作示意之用
- {
- return malloc(size) ;
- }
- void operator delete(void pp) 非严格实现,仅作示意之用
- {
- free(pp) ;
- }
- public
- NoHashObject()
- {
- 此处可以获得需要封装的资源,并让ptr指针指向该资源
- ptr = new Resource() ;
- }
- ~NoHashObject()
- {
- delete ptr ; 释放封装的资源
- }
- };
NoHashObject现在就是一个禁止C++堆对象的类了,如果你写下如下代码:
- NoHashObject fp = new NoHashObject() ; 编译期错误!
- delete fp ;
上面代码会产生编译期错误。好了,现在你已经知道了如何设计一个禁止堆对象的类了,你也许和我一样有这样的疑问,难道在类NoHashObject的定义不能改变的情况下,就一定不能产生该类型的C++堆对象了吗?不,还是有办法的,我称之为“暴力破解法”。C++是如此地强大,强大到你可以用它做你想做的任何事情。这里主要用到的是技巧是指针类型的强制转换。
- void main(void)
- {
- char temp = new char[sizeof(NoHashObject)] ;
强制类型转换,现在ptr是一个指向NoHashObject对象的指针
- NoHashObject obj_ptr = (NoHashObject)temp ;
temp = NULL ; 防止通过temp指针修改NoHashObject对象
再一次强制类型转换,让rp指针指向堆中NoHashObject对象的ptr成员
- Resource rp = (Resource)obj_ptr ;
初始化obj_ptr指向的NoHashObject对象的ptr成员
- rp = new Resource() ;
现在可以通过使用obj_ptr指针使用堆中的NoHashObject对象成员了
- ... ...
- delete rp ;释放资源
- temp = (char)obj_ptr ;
- obj_ptr = NULL ;防止悬挂指针产生
- delete [] temp ;释放NoHashObject对象所占的堆空间。
- }
上面的实现是麻烦的,而且这种实现方式几乎不会在实践中使用,但是我还是写出来路,因为理解它,对于我们理解C++内存对象是有好处的。对于上面的这么多强制类型转换,其最根本的是什么了?我们可以这样理解:
某块内存中的数据是不变的,而类型就是我们戴上的眼镜,当我们戴上一种眼镜后,我们就会用对应的类型来解释内存中的数据,这样不同的解释就得到了不同的信息。
所谓强制类型转换实际上就是换上另一副眼镜后再来看同样的那块内存数据。
另外要提醒的是,不同的编译器对对象的成员数据的布局安排可能是不一样的,比如,大多数编译器将NoHashObject的ptr指针成员安排在对象空间的头4个字节,这样才会保证下面这条语句的转换动作像我们预期的那样执行:
- Resource rp = (Resource)obj_ptr ;
但是,并不一定所有的编译器都是如此。既然我们可以禁止产生某种类型的C++堆对象,那么可以设计一个类,使之不能产生栈对象吗?当然可以。