从事实时计算多年,熟悉jstorm/spark/flink/kafka/rocketMq, 热衷于开源,希望在这里和前辈们一起学习与分享,得到长足的进步!邮箱:hustfxj@gmail.com 我的githup地址是:https://github.com/hustfxj。欢迎和大家一起交流探讨问题。
分类: C/C++
2014-03-03 20:09:08
1. 只能在堆(heap)上创建对象/禁止产生栈(stack)对象
创建栈对象时会移动栈顶指针以“挪出”适当大小的空间, 再在这个空间上直接调用对应的构造函数以形成一个栈对象, 而当函数返回时会调用其析构函数释放这个对象, 再调整栈顶指针收回那块栈内存, 在这个过程中是不需要operator new/delete操作的, 所以将operator new/delete设置为private不能达到禁止产生栈(stack)对象的目的.
把析构函数定义为private访问权限, 就可以保证只能在堆(heap)上创建(new)一个新的类对象.析构函数私有化的类的设计可以保证只能用new命令在堆(heap)中创建对象, 只能动态的去创建对象, 这样可以自由的控制对象的生命周期, 但这样的类需要提供创建和撤销的公共接口.
class OnlyHeapClass { public: OnlyHeapClass() { } void Destroy() { delete this; // 等效于"OnlyHeapClass::~OnlyHeapClass();", 写 // 成"OnlyHeapClass::~OnlyHeapClass();"更容易理 // 解public成员函数调用private析构函数. } private: ~OnlyHeapClass() { } }; int main() { OnlyHeapClass *pInst = new OnlyHeapClass; pInst ->Destroy(); // 如果类中没有定义Destroy()函数, 而在这里用"delete pInst;"代 // 替"pInst->Destroy();", 则会报错. 因为"delete pInst;"会去调 // 用类的析构函数, 而在类域外调用类的private成员函数必然会报错. return 0; }
解析:C++是一个静态绑定的语言. 在编译过程中, 所有的非虚函数调用都必须分析完成. 即使是虚函数, 也需检查可访问性. 在栈(stack)上生成对象时, 对象会自动析构, 即析构函数必须可以访问. 而在堆(heap)上生成对象, 由于析构时机由程序员控制, 所以不一定需要析构函数.
将析构函数设为private除了会限制栈对象生成外, 还会限制继承. 如果一个类不打算作为基类, 通常采用的方案就是将其析构函数声明为private. 为了限制栈对象, 却不限制继承, 可将析构函数声明为protected, 这样就两全其美了. 如下代码所示:
class NoStackObject { protected: ~NoStackObject() { } public: void destroy() { delete this ;//调用保护析构函数 } };
接着, 可以像这样使用NoStackObject类:
NoStackObject* hash_ptr = new NoStackObject(); ... ... //对hash_ptr指向的对象进行操作 hash_ptr->destroy();
是不是觉得有点怪怪的: 用new创建一个对象, 却不是用delete去删除它, 而是要用destroy方法, 用户是不习惯这种怪异的使用方式的, 所以将构造函数也设为private或protected, 但需要让该类提供一个static成员函数专门用于产生该类型的堆对象. (设计模式中的singleton模式就可以用这种方式实现. )让我们来看看:
class NoStackObject { protected: NoStackObject() { } ~NoStackObject() { } public: static NoStackObject* creatInstance() { return new NoStackObject() ;//调用保护的构造函数 } void destroy() { delete this ;//调用保护的析构函数 } }; NoStackObject* hash_ptr = NoStackObject::creatInstance(); ... ... //对hash_ptr指向的对象进行操作 hash_ptr->destroy(); hash_ptr = NULL; //防止使用悬挂指针
2. 只能在栈(stack)上生成对象
为禁止产生某种类型的堆对象, 可以自己创建一个资源封装类, 该类对象只能在栈中产生, 这样就能在异常的情况下自动释放封装的资源.
#include// 需要用到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* fp = new NoHashObject(); // 编译期错误! delete fp;
int main() { 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对象所占的堆空间. return 0; }