转载于: http://blog.csdn.net/hxz_qlh/...
如遇侵权,麻烦联系删除面试
昨天一个同窗去网易面试C++研发,问到了这么一个问题:如何限制一个类对象只在栈(堆)上分配空间?函数
通常状况下,编写一个类,是能够在栈或者堆分配空间。但有些时候,你想编写一个只能在栈或者只能在堆上面分配空间的类。这能不能实现呢?仔细想一想,其实也是能够滴。this
在C++中,类的对象创建分为两种,一种是静态创建,如A a;另外一种是动态创建,如A* ptr=new A;这两种方式是有区别的。.net
静态创建类对象:
是由编译器为对象在栈空间中分配内存,是经过直接移动栈顶指针,挪出适当的空间,而后在这片内存空间上调用构造函数造成一个栈对象。使用这种方法,直接调用类的构造函数。指针
动态创建类对象:
是使用new运算符将对象创建在堆空间中。这个过程分为两步,第一步是执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。code
那么如何限制类对象只能在堆或者栈上创建呢?下面分别进行讨论。对象
就是不能静态创建类对象,即不能直接调用类的构造函数。blog
容易想到将构造函数设为私有。在构造函数私有以后,没法在类外部调用构造函数来构造类对象,只能使用new运算符
来创建对象。然而,前面已经说过,new运算符
的执行过程分为两步,C++
提供new运算符
的重载,实际上是只容许重载operator new()
函数,而operator new()
函数只用于分配内存,没法提供构造功能。所以,这种方法不能够。继承
当对象创建在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。若是编译器没法调用类的析构函数,状况会是怎样的呢?好比,类的析构函数是私有的,编译器没法调用析构函数来释放内存。因此,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。若是类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。生命周期
所以,将析构函数设为私有,类对象就没法创建在栈上了。
代码以下:
class A { public: A(){} void destory(){delete this;} private: ~A(){} };
试着使用A a;
来创建对象,编译报错,提示析构函数没法访问。这样就只能使用new操做符
来创建对象,构造函数是公有的,能够直接调用。类中必须提供一个destory
函数,来进行内存空间的释放。类对象使用完成后,必须调用destory
函数。
若是A做为其它类的基类,则析构函数一般要设为virtual
,而后在子类重写,以实现多态。
所以析构函数不能设为private
。
还好C++
提供了第三种访问控制,protected
。
将析构函数设为protected
能够有效解决这个问题,类外没法访问protected
成员,子类则能够访问。
使用new
创建对象,却使用destory
函数释放对象,而不是使用delete
。
(使用delete
会报错,由于delete
对象的指针,会调用对象的析构函数,而析构函数类外不可访问。这种使用方式比较怪异。)
为了统一,能够将构造函数设为protected
,而后提供一个public
的static
函数来完成构造,这样不使用new
,而是使用一个函数来构造,使用一个函数来析构。
代码以下,相似于单例模式:
class A { protected: A(){} ~A(){} public: static A* create() { return new A(); } void destory() { delete this; } };
这样,调用create()函数在堆上建立类A对象,调用destory()函数释放内存。
只有使用new运算符
,对象才会创建在堆上,所以,只要禁用new运算符就能够实现类对象只能创建在栈上。
虽然你不能影响new operator
的能力(由于那是C++语言内建的),可是你能够利用一个事实:new operator
老是先调用 operator new
,然后者咱们是能够自行声明重写的。
所以,将operator new()
设为私有便可禁止对象被new
在堆上。
代码以下:
class A { private: void* operator new(size_t t){} // 注意函数的第一个参数和返回值都是固定的 void operator delete(void* ptr){} // 重载了new就须要重载delete public: A(){} ~A(){} };