C++中限制对象的申请
最近阅读Mitsuba的架构,有一个挺有意思的设计,开始没看明白。
下面,我把限制对象的申请分两个方面讲述:
1.限制对象在堆/栈上申请;
2.限制对象申请的个数。
//所有的资料,都可以在《more effective c++》上找到
有一个基类Object,它的析构函数是protected访问权限的。并且,所有继承Object的类,都需要实现一个自己的protected的析构函数。
Mitsuba还特意解释了,这是为了防止在stack上构建Object的对象。
这是怎么回事呢?
”在栈上构建的对象有什么特点呢?首先,在定义对象的地方会自动构造,并在生命周期结束时自动析构。所以,只要让那些被隐式调用的构造动作和析构动作不合法,就可以了“
采用protected的目的是为了能够让类能够正常的被继承。但是!要保持子类也不能在栈上申请对象,必须保持子类的析构函数也是protected/private(若是private子类不能被继承,更多信息查看 http://www.cnblogs.com/wangpei0522/p/4460425.html)
我们把析构函数设为了不可访问的protected/private,直接调用delete操作符释放资源不会通过编译的!
为此,我们必须增加一个成员函数,在成员函数中使用delete释放资源。因为在成员函数内部,类的成员变得总是可见,所以delete能够成功执行。(Mitsuba中通过智能指针的方式释放资源。非常巧妙的设计)
与防止在stack上构建对象的设计所对应的方案是
防止在heap上构建对象
亲爱的朋友,你肯定能想到设置operator new()的访问权限设置为protected/private,是吧?
可是有一个问题:
class onlyStack{ protected: void* operator new (size_t s) { } void operator delete(void* ptr) { } private: int base_data; }; class sets{ private: onlyStack s; };
sets* sets_array = new sets[100];
上面的代码中,sets_array的定义语句发生了如下过程:
调用sets的operator new申请了一块heap上的内存,然后调用调用sets的构造函数,sets的构造函数再调用了onlyStack的构造函数。全程没有用到onlyStack的operator new。
我们并没有很好的办法解决这个问题,详见《more effective C++》 #27条款
^.^
限制对象的个数
什么是限制对象的个数?
首先,如果我们限制对象可申请的个数为0的时,也就等价于不能申请对象。
每当产生一个对象,它的构造函数都会被调用,所以,如果我们声明它的构造函数为private,此对象便不能显示申请(这里的显示申请意味着,我们可以通过别的方式申请它,比如静态成员函数调用了构造函数,此成员函数就能申请此对象)。
还有什么?是的,抽象类也不能申请对象。我们可以定义一个纯虚函数,此对象就成为抽象类,便不能申请对象。
接下来,我们限制对象可申请的个数为1的时,也就是大名鼎鼎的单例模式。
继续扩展,我们限制对象可申请的个数为k,k是大于1的正整数。注意,此时的语义和前面的两个不太一样。可申请个数为0和1,只会出现不能申请和每次申请都只会那唯一的一个对象。
也就是说,当我们申请的对象个数大于k的时候,会出现错误,或者处理错误的行为,而不是返回已经存在的k个对象中的某一个。
简单说明下原理,我们在类中增加一个计数的数据成员,每次构造函数被调用就加一,析构函数被调用就减一。如果构造函数发现当前的计数成员已经等于最大值k,跳转到处理错误的情况。
在《more effective c++》的条款26有详细的讲解,但是,我觉得他讲得很繁琐,特别是考虑继承的情况。被继承后,应该要根据具体的应用环境来确定计数。
记得借鉴的是,通过private继承一个base counting class,该class是一个template类,为每个需要计数的class生成一套代码。
以上。