如何定义一个只在栈/堆上生成的对象

来源:互联网 发布:淘宝投诉电话是多少呢 编辑:程序博客网 时间:2024/06/08 16:31

一:首先复习下内存结构;
1:栈(stack):–由编译器自动分配释放,存放函数参数值,局部变量值等.
2:堆(heap):一般是由程序员分配释放,若程序不释放,程序结束时可能由操作系统回收
3:全局区(静态区):主要存放全局变量和静态变量,分为已初始化的全局变量和静态变量区,data区,未初始化的全局变量和静态变量区(bbs区);
4:常量区:各种常量字符
5:代码段:存放函数二进制代码(code区);

二:C++类的创建的方式
c++中,类的建立分为两种,一种是静态建立,如A a,另一种是动态建立,如A*ptr = new A;这两种方法是有区别的
1:静态建立类对象:是由编译器为对象在栈空间分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象,这种方法直接调用类的构造函数

2:动态建立类的对象,是使用new运算符将对象建立在堆空间中,这个过程分为两步,第一步是执行operator new[]函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间,这种方法,间接调用类的构造函数.
三:定义一个只能在堆上分配的对象
就是不能静态建立对象,即不能直接调用类的的构造函数

这时我们很快想到将类的对象定义成私有,这样就无法调用构造函数来构造类的对象,只能通过new运算符来建立对象,然而,前面我们了解到new运算符执行有两步,C++提供new运算符的重载,其实是只允许重载operator new()函数,而operator new()函数只用于分配内存,无法提供构造功能,因此这种方法不可以.

这时我们考虑将对象建立在栈上面,是由编译器分配内存空间的,调用构造函数来构造栈对象.当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间,编译器管理了对象的整个生命周期.如果编译器无法调用类的析构函数,比如,类的析构函数是私有的,编译器无法调用析构函数来释放内存,所以,编译器为类对象分配栈空间时会先考虑析构函数访问的 可能性,其实不光是析构函数,只要是非静态的函数,编译器都会检查.如果类的析构函数是私有的,则编译器不会在栈空间上分配内存,因此,将析构函数设为私有,类对象就无法建立在栈上了;
方法一:

#include<iostream>using namespace std;class A{public:    A()    {}    void Clean()    {        delete this;    }private: ~A()    {}};int main(){    A a;    return 0;}

这里写图片描述
我们发现编译会报错,提示析构函数无法访问,这样只能使用new 操作符来建立对象,构造函数是公有的,可以直接调用. 类中必须提供一个destory函数,来进行内存空间的释放。类对象使用完成后,必须调用destory函数。

缺点是:
1:无法解决继承问题
2:类的使用不符合规则,使用new 创建的对象,调用自定义函数Clean来释放对象,而不是使用delete(使用delete会报错,因为delete对象的指针,会调用对象的析构函数,而析构函数不可访问.)如何解决,可以将构造函数设为保护,然后提供一个公有的static函数来完成构造,这样不使用new,而是使用一个函数来构造,使用一个函数来析构.
方法二:

class A{protected:   A()    {}public:  static A*Create()    {        return new A();    }    void Clean()    {        delete this;    }};int main(){    A* ht4 =  A::Create();    A ht5(*ht4);    return 0;}

四:定义一个只能在栈上生成的对象
只有使用new运算符,对象才会建立在堆上,因此只要禁用new 运算符就可以实现类对象只能建立在栈上.虽然不能影响new operator的能力,但是new operator总是先调用operator new,而后面我们是可以自行声明重写的.因此,将operator new()设为私有即可禁止对象被new在堆上.

class B{  void *operator new(size_t );void operator delete(void *ptr);public:  B(){}~B(){}};int main(){   B b;    return 0;}

五:栈和堆的区别
1:申请方式:栈是系统自动申请和 释放,堆则由程序员显示申请
2:申请和释放堆内存的API在C语言中是malloc和free,在C++中是new/delete.申请与释放一定要配对使用,否则就会出现内存泄露,时间长了就会出现OOM(out of memmory)错误.

一般在return /exit或者break/continue这些语句容易忘记释放内存,所以要检查内存泄露时要关注这些语句,前他们是否有必要的释放语句free/delete

3:所占空间:堆的空间比较大,栈比较小,所以申请大的内存一般在堆中申请,栈不要用太大的内存结构,比如大的静态数组,否则不容易出现栈溢出的情况

4:关于生命周期:栈较短(随着函数退出或者返回)也就是栈帧的结束而终止.本函数的栈完成了使用,堆要在什么时候释放,生命周期就在什么时候结束
5:栈是向下生长,堆是向上生长的.

部分参考:http://blog.csdn.net/hxz_qlh/article/details/13135433

阅读全文
0 1
原创粉丝点击