1.53、如何保证类的对象只能被开辟在堆上?

1. 将构造函数声明为私有或受保护,禁止外部直接在栈上或作为全局/静态对象创建实例,因为这些场景都需要调用构造函数,而私有构造函数阻止了这种调用。

2. 提供一个public的静态工厂函数(如CreateInstance)来返回堆上对象的指针,该函数内部使用new操作符创建对象。

3. 将析构函数声明为protected或私有,防止外部直接delete对象,避免误用。

4. 提供一个成员函数(如destroy)来负责delete自身,确保对象生命周期由类自身控制,避免内存泄漏。

这样,用户只能通过工厂函数获得堆上对象,且不能在栈上或静态区创建实例,且对象销毁也受控,类似于单例模式的管理方式。

为什么不能通过重载operator new禁止堆上分配来保证只能堆分配?

  • • operator new只是负责内存分配,不负责对象构造。构造函数的访问权限才是关键。
  • • 如果将operator new重载为不可用,只能禁止堆分配,反而允许栈分配。反之,私有构造函数禁止栈分配,工厂函数配合new实现堆分配。

因此,控制构造函数访问权限是根本,operator new重载是辅助手段。

代码结构:

class HeapOnly {
public:
    static HeapOnly* CreateInstance() {
        return new HeapOnly();
    }

    void destroy() {
        delete this;
    }

protected:
    ~HeapOnly() {}  // 保护析构函数,防止外部delete

private:
    HeapOnly() {}   // 私有构造函数,禁止栈上或全局创建
};

这样,HeapOnly obj;HeapOnly* p = new HeapOnly;都会编译错误,唯一合法方式是HeapOnly* p = HeapOnly::CreateInstance();

通过p->destroy();安全释放堆内存。

这种设计不仅保证了对象只能在堆上创建,还能控制对象生命周期,避免栈上临时对象或全局静态对象带来的管理复杂性,适合需要严格控制内存管理和生命周期的场景。
本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
(加入我的知识星球,免费获取账号,解锁所有文章。)

阅读剩余
THE END