如何创建一个类只能在堆(栈)上建立对象

来源:互联网 发布:美国大学不要面试知乎 编辑:程序博客网 时间:2024/06/06 07:44


  在C++中,类对象的建立方式有两种,一种是静态建立类对象,如A a;另一种是动态建立类对象,如A* p = new A;


这两种方式是有区别的:

    静态建立类对象:是指全局对象,静态对象,以及分配在栈区域内的对象,编译器对它们的内存分配是在编译阶段就完成的,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。

    动态建立类对象:分配堆区域内的对象,编译器对他们的内存分配是在运行时动态分配的,(使用new运算符将对象建立在堆空间中。这个过程分为两步,第一步,执行operator new()函数,在对中搜索合适的内存并进行分配,第二步,调用构造函数构造对象,初始化这片内存空间。使用这种方法,间接调用类的构造函数。


知道了这些,那么,如何限制类只能在堆上或栈上建立对象呢?


1.只能在堆上建立对象  

    类对象只能建立在堆上,就是不能静态建立对象,即不能直接调用类的构造函数。

    很容易就想到把类的构造函数声明为私有,就无法在类的外部调用类的构造函数建立对象,只能使用new运算符来建立对象。前面已经说过,new运算符的执行过程分为两步,C++提供new运算符的重载其实只是允许重载operator new()函数,不能重载new运算符,而operator new()函数只用于分配内存,无法提供构造函数,所以,我们再定义一个GetObj函数,用于在堆上new对象,通过GetObj函数,建立的对象都是在堆上的new出来的,将函数声明为静态的,就可以通过域作用访问符访问GetObj函数,在堆上建立对象。(在C++中静态成员函数也是类函数,及这个函数不属于某个具体的对象,而是属于一个类的,这个类实例化的每个成员都可用,同时,这个类也可以直接调用这个函数而不用实例化一个对象。)

<span style="font-family:SimSun;font-size:18px;">class BB{public:static BB& GetObj(int b1 = 0,int b2 = 0){return *(new BB(b1,b2));}private:BB(int b1 = 0,int b2 = 0):_b1(b1),_b2(b2){}int _b1;int _b2;};void Test(){BB b = BB::GetObj();}</span>


网上还有一种说法,就是将析构函数声明为私有的,当对象建立在栈上面的时候,是由编译器分配内存空间

的,调用构造函数来构造栈对象,当对象使用使用完之后编译器会调用析构函数来释放栈对象,编译器管理了对象的

整个生命周期,如果编译器无法调用析构函数来释放空间,那会发生什么情况呢?比如,类的析构函数是私有的,编

译器无法调用析构函数来释放内存。所以,编译器在为类对象分配栈空间的时候,会先检查类的析构函数的访问性,

不只是析构函数,只要是非静态的函数,编译器都会检查,如果类的析构函数是私有的,那么编译器就不会在栈上位

类对象分配内存。


那么,到底为什么将析构函数声明为私有的就可以只在堆上建立对象呢?

因为c++是一个静态绑定的语言,在编译过程中,所有的非虚函数都必须分析完成,即使是虚函数,也需检查可访问

性,因此,当在栈上生成对象是,对象会自动析构,也就是说析构函数必须可访问,而对上生成对象,由于析构是由

程序员控制,所以,就可以不用析构函数,而重新定义一个函数完成析构函数的功能,然后手动调用。

class AA{public:AA(){}protected:~AA(){}};void Test(){AA* a = new AA;}

那么怎么释放它呢?  析构函数私有化的类的设计可以保证只能用new命令在堆(heap)中创建对象, 只能动态的去创建对象, 这样可以自由的控制对象的生命周期. 但是, 这样的类需要提供创建和撤销的公共接口. 

AA::void Destory(){delete this;}

   上述方法的一个缺点就是,无法解决继承问题。如果A作为其它类的基类,则析构函数通常要设为virtual,然后在子类重写,以实现多态。因此析构函数不能设为private。还好C++提供了第三种访问控制,protected。将析构函数设为protected可以有效解决这个问题,类外无法访问protected成员,子类则可以访问。

这就证明了,如果要创建一个只能在堆上建立对象的类,无论将它的构造函数声明成私有还是析构函数声明为保护都可以达到这样的目的,但是,如果将该类的构造函数或析构函数声明为保护,最好将另外一个也声明为保护,因为例如你用new创建一个对象, 却不是用delete去删除它, 而是要用destroy方法,很显然,这是一种很奇怪的使用方法,所以,如果将该类的构造函数或析构函数声明为保护,最好将另外一个也声明为保护。

2.只能在栈上建立对象的类

只有使用new运算符,对象才会建立在堆上,所以只要禁用new运算符就可以实现类对象只能建立在栈上,将operator new()设为私有就可以了,一定要记得,重载了new就要重载delete

class CC{public:CC(){}~CC(){}private:void* operator new(size_t)//重载operator new() ,注意函数返回值和参数都是固定的{}void operator delete(void* ptr)//也要重载operator delete(){}};

参考:http://blog.csdn.net/g5dsk/article/details/4775144




1 0
原创粉丝点击