一般的,对象的创建分两种方式,一种是在栈上创建对象,另一种是在堆上创建对象,下面我们就从这两个方面来讨论如何限制用户操作。
1 只允许用户在堆上创建对象
所谓只允许用户在堆上创建对象,就是只允许用户使用new操作符来创建对象,换句话说要禁止用户在栈上创建对象。
也许你会想,根据类的构造函数和访问限制特点,可以将构造函数声明为私有,但是这样一来,new操作符也不能访问构造函数了。
class X { //... }; X gObject;//line1 void test() { X lObject;//line2 X* ptr= new X;//line3 }
针对以上代码,我们希望”line1″和”line2″编译错误,”line3″编译正确,那么我们就需要对类的特殊函数做一些处理。
上面已经说到了,构造函数是不能声明为私有的,否则也会阻止在堆上创建对象,既然这样,构造函数就只能声明为公有了,还有什么方法可以阻止在栈上创建呢。
我们再仔细想一想,在C++中,有这样一条规则:当用户试图在栈上创建对象时,编译器会查找匹配且可以访问的构造函数和析构函数,如果其中一个无法访问,编译就会报错。
这似乎给了我们一点提示,将析构函数声明为私有,这样的话不就可以禁止用户在栈上创建对象了吗。
class X { public: X(){} void Delete() { delete this; } private: ~X(){} }; X gObject;//line1 void test() { X lObject;//line2 X* ptr= new X;//line3 }
我们编译一下,发现符合预期。
但是,马上下一个问题来了,如果你使用delete来删除创建出来的对象时,编译器还是报错了。
为此,我们可以再编写一个释放函数,在函数中调用delete操作符,这样所有的问题就都解决了。
下面是示例代码:
class X { public: X(){} void Delete() { delete this; } private: ~X(){} }; X gObject;//line1 void test() { X lObject;//line2 X* ptr= new X;//line3 //... ptr->Delete(); }
好了,大功告成,但是,有一点需要注意,我们在成员函数Delete()中,删掉了this指针所指对象,这有点像对象的自杀行为,如果该对象再被引用将会报错,所以请确定不再使用该对象时,再调用删除函数,这个地方一定要注意。
那么我们可以总结:使析构函数私有化,可以禁止在栈上创建对象。
2 只允许在栈上创建对象
这跟我们上一小节刚好相反,我们需要禁止new操作符创建对象,聪明的同学可能已经想到了,那就是将new和delete操作符声明为私有的即可。
class Y { public: Y(){} private: void* operator new(size_t size); void operator delete(void* addr); }; void test() { Y obj;//line1 Y* ptr= new Y;//line2 }
编译一下,正如我们所预想的,”line2″将不能通过编译,这就阻止了用户在堆上创建对象,是不是很神奇。
好了,关于今天的分享就到这里了,尽管这样的技巧在平时用的不多,但是在某些场景下的确是很好的处理办法。
例如,如果你正在编写一个同步锁或文件对象等,为了让锁自动打开和释放或者是让文件自动打开和关闭,你就使该类实例只能在栈上创建,在构造和析构函数中编写初始化与释放代码,这样用户创建的类对象就会自动管理资源,从而你也不必担心用户因为忘记释放资源而造成的种种坑了。