C++只在栈或堆上实例化对象
C++如何让类对象只能在堆(栈)上分配空间
一般情况下写一个类都是可以采用new在堆上分配空间,或直接采用 类名+对象名 的方式在栈上分配空间。但有时候,如果想让一个类只能在栈上或者堆上分配空间,又该怎么实现呢?
下面分别来介绍如何定义只能在栈上或堆上实例化的类。
注:
1.静态实例化对象 如 A a;的步骤为:先由编译器为对象在栈空间分配内存,移动栈顶指针,挪出适当的空间,然后在这个空间上调用构造函数形成一个对象,在对象使用完之后,调用析构函数回收内存,栈顶指针减一。
2.动态实例化对象:new操作符,第一步执行operator new()函数在堆上分配一个内存,然后调用构造函数初始化这片空间。
只能在栈上分配类对象
只有使用new运算符,对象才会建立在堆上,因此要禁用new运算符才能只在栈上分配空间。但new操作符是C++内建的,所以必须要先认清一个事实即:new operator总是先调用operator new,所以我们只要堆new操作符进行重载,并将它声明为private的,就能保证不能再使用new实例化对象,如:
class A{ private: void* operator new(size_t t){} void operator delete(void* ptr){} public: A(); ~A(); };
那么此时就不能再调用new操作符了。
只能在堆上实例化对象
容易想到的方法是将构造函数私有化,那么在类外就不能实例化对象,只能在类内提供一个共有函数使用new运算符返回一个对象,这也是典型的单例模式的由来,但在类外还是不能使用new操作符进行实例化。所以这种方法是行不通的。
所以考虑析构函数的私有化。因在栈上实例化对象之后,对象使用完毕之后自动调用析构函数,而new对象后,要手动调用delete函数才能执行对象的析构函数。
所以当析构函数被私有化之后,若在栈上实例化对象,编译器先会检查该对象的析构函数是否可用,如果不可用,则会报错。在堆上释放对象时,若不调用delete就不会发现析构函数不可访问。
这就会引发另外一个问题,使用new操作符在堆上实例化的对象要怎么析构呢?解决办法就是,在类中自定义一个公有函数用来销毁对象,该函数调用delete操作符,这样就可以使用该函数销毁对象而不是直接使用delete操作符了。
如:
class A{ public: A(){} void destroy(){delete this;} private: ~A(){} };
在此,就可以使用new在堆上创建对象而使用destroy()析构对象了。
注意:采用这种方式后,类A不能用在继承体系当中。
若A为基类,那么其析构函数就是virtual的(保证内存完全释放),子类必须要能重写该析构函数,但子类根本无法访问他,这是其一。其二,即使子类能够重写析构函数,若B是继承自A的子类,且声明了一个B类的对象b,那么此时若析构B,基类A不能被正确释放,导致内存泄露。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
2019-03-26 STM32 DMA使用详解
2019-03-26 为什么stm32有的外设在进行初始化的时候需要将寄存器重设为缺省值?不设置会怎么样?