设计一个类只能在堆(栈)上创建

本文介绍了C++中如何仅允许在堆或栈上创建对象的方法。对于只能在堆上创建的对象,可以通过保护构造函数和提供静态创建/销毁方法实现;而只能在栈上创建的对象,则可以通过使new和delete操作符私有化来达到目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在C++中,创建类的对象有两种方法,一种是静态建立,A a; 另一种是动态建立,调用new 操作符。

静态建立一个类对象,是由编译器为对象在栈空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。

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

1、只能在堆上创建对象的类

那就是动态建立类的对象,使用new操作符来完成。
所以可以这样做,将该类的构造函数和析构函数权限设为protected,(可以让该类可以被继承),然后定义两个static 函数来调用new ,delete 来创建和销毁对象。

//设计一个类只能在堆上创建对象
//在堆上创建对象,需要使用new,使用静态成员函数创建对象和销毁
//将构造函数和析构函数声明为protected,是为了让该类可以被继承
class A
{
protected:
    A(){}
    ~A(){}
public:
    static A* Create()
    {
        return new A();
    }
    static void Destroy(A* p)
    {
        delete p;
        p = NULL;
    }
};

方法二:将析构函数声明为私有的

这是我在网上看到的另一种方法

为什么将析构函数设为私有的,就可以只能在堆上创建?

对象建立在栈上面时,是由编译器分配空间的,调用构造函数来构造栈对象,当对象使用完之后,编译器会调用析构函数来释放栈对象所占的空间,编译器管理了对象的整个生命周期,编译器为对象分配空间的时候,只要是非静态的函数都会检查,包括析构函数,但是此时析构函数不可访问,编译器无法调用类的析构函数来释放内存,那么编译器将无法在栈上为对象分配内存。

那么如何释放它呢? 答案也很简单, 提供一个成员函数, 完成delete操作. 在成员函数中, 析构函数是可以访问的, 当然detele操作也是可以编译通过.

class a
{
public :
    a(){}
    void destory()
    {
        delete this;
    }
private:
    ~a(){};
};

方法二两个缺点:(1)无法解决继承问题,因为通常情况之下a作为基类,一般析构函数要设为vitual,然后子类重写,已实现多态,因此析构函数不能设为private,不过c++还有protected访问控制方式,将析构函数设置为protected,这样子类可以访问,但是类外无法访问。(2)使用不方便,不统一,因为你使用了new创造了对象,但是不能使用delete释放对象,必须使用destory函数,这种方式比较怪异,所以我们也可以将构造函数设置为protected,同时提供另一public static create()函数来进行替代new。这样 create()创建对象在堆上, destory()释放内存。
这样 其实 还是和方法一差不多。

2、只能在栈上创建对象的类

只能在栈上创建的对象的话,就是不能调用new 操作符,所以可以将operator new 和operator delete 设置为私有的。

//设计一个类只能在栈上创建对象,
//不能调用new 操作符,重载operator new() 和 operator delete()

class B
{
private:
    void * operator new(size_t size){}
    void operator delete(void *ptr){}

public:
    B(){}
    ~B(){}
};
<think>好的,用户想设计一个C++,使得该的对象只能堆上创建。我需要回忆一下相关的知识,并参考提供的引用资料来给出正确的实现方法。 首先,C++中对象的创建有两种方式:上(静态创建)和堆上(动态分配)。上的对象通过直接声明,比如`A a;`,而堆上的对象则使用`new`操作符,比如`A* ptr = new A();`。要限制对象只能堆上创建,就必须阻止用户在创建对象。 根据引用[1]和[2],通常的做法是将析构函数设为私有或者受保护。因为当对象在创建时,编译器会在作用域结束时自动调用析构函数。如果析构函数不可访问(比如私有),那么上对象的创建会导致编译错误。但是,这样做之后,用户也无法直接使用`delete`来释放堆上的对象,所以需要提供一个公共的销毁方法,比如`destory()`,内部调用`delete this`。或者,可以将析构函数设为私有,并提供一个公有的`delete`方法。 另外,引用[3]提到,还有一种方法是重载`new`和`delete`操作符,但可能不太常见。不过更常见的是控制构造函数和析构函数的访问权限。 可能的步骤: 1. 将析构函数设为私有或受保护,阻止上的自动析构。 2. 提供静态的创建方法,返回对象的指针,比如`create()`方法,内部调用`new`。 3. 确保用户无法直接调用构造函数,所以构造函数也需要设为私有或受保护。 4. 可能需要禁用拷贝构造和赋值操作,避免通过其他方式创建对象。 例如,A的构造函数和析构函数设为私有,然后提供一个静态的create函数,返回A*。这样用户只能通过A::create()来生成对象,而无法直接实例化或者在创建。 还需要考虑拷贝的问题,防止用户复制对象导致意外创建。因此,应该将拷贝构造函数和赋值运算符声明为删除或者私有,并不予实现。 参考引用[3]中的例子,正确的做法是析构函数私有,构造函数公有的话可能有问题?或者构造函数也设为私有,然后通过静态成员函数来创建对象。 可能存在的矛盾点:如果构造函数是私有的,那么用户无法直接调用new,因为new会调用构造函数,但构造函数不可访问。所以这时候必须通过一个静态成员函数来调用new,因为静态成员函数可以访问私有构造函数。 因此,正确的做法应该是: - 构造函数设为私有或受保护。 - 析构函数设为私有或受保护。 - 提供公共的静态成员函数来创建对象,返回指针。 - 提供公共的销毁方法,或者重载operator delete,但更常见的是提供一个destroy函数。 例如: class HeapOnly { public: static HeapOnly* create() { return new HeapOnly(); } void destroy() { delete this; } private: HeapOnly() {} ~HeapOnly() {} }; 这样用户只能通过HeapOnly::create()创建对象,而无法在上构造,因为构造函数和析构函数都是私有的。同时,用户必须调用destroy来释放对象,或者可以结合智能指针来管理。 此外,还要防止用户通过拷贝构造函数或赋值操作来创建对象,所以需要将拷贝构造函数和赋值运算符设为删除或者私有。 可能还需要禁用移动构造函数等,但根据C++11后的标准,可以显式地删除这些函数。 总结步骤: 1. 私有构造函数和析构函数。 2. 静态工厂方法创建对象。 3. 公共销毁方法。 4. 禁用拷贝和赋值。 这样就能确保对象只能堆上创建。 另外,引用[2]中提到,可以通过将析构函数私有化,这样对象无法自动析构,导致编译错误。而对象需要用户自己调用delete,但由于析构函数不可访问,所以必须提供一个public的destroy方法,里面执行delete。 例如: class HeapOnly { public: void destroy() { delete this; // 需要析构函数可访问,所以析构函数需要是public?或者destroy是成员函数,可以访问私有析构函数? } private: ~HeapOnly() {} }; 但这样的话,构造函数如果是public,用户仍然可以在创建对象,但无法析构,导致编译错误。但构造函数如果是public,用户可以在上构造对象,但无法析构,所以在编译阶段就会报错。 比如用户写HeapOnly obj; 会触发构造函数,而当离开作用域时,编译器尝试调用析构函数,但因为是私有的,所以报错。这样构造函数可以是public的,但析构函数是私有的。但这样用户无法直接调用析构函数,所以无法delete,必须通过destroy方法。 不过,此时用户仍然可以调用new HeapOnly(),但无法delete,所以必须通过destroy函数。 但这种方法的问题在于,构造函数如果是public的,用户还是可以在创建对象,尽管析构时会失败。所以更安全的方法是将构造函数设为私有,这样无论是还是,用户都无法直接构造对象,必须通过静态方法创建。 所以正确的做法应该是: 构造函数和析构函数都设为private,然后提供静态方法创建对象,并提供一个destroy方法。 例如: class HeapOnly { public: static HeapOnly* create() { return new HeapOnly(); } void destroy() { delete this; } private: HeapOnly() {} ~HeapOnly() {} }; 这样用户无法在创建对象,因为构造函数不可访问。也无法直接调用new HeapOnly(),因为构造函数是私有的,只能通过静态方法create来创建对象。 此时,用户必须通过HeapOnly* obj = HeapOnly::create(); 来创建对象,然后通过obj->destroy()来释放。 这种情况下,拷贝构造函数和赋值运算符应该被禁用,否则用户可能通过拷贝创建对象。例如: HeapOnly* obj1 = HeapOnly::create(); HeapOnly obj2 = *obj1; // 调用拷贝构造函数,但如果是私有的,就会报错。 因此,需要将拷贝构造函数和赋值运算符设为private或者delete: class HeapOnly { public: static HeapOnly* create() { return new HeapOnly(); } void destroy() { delete this; } // 删除拷贝构造函数和赋值运算符 HeapOnly(const HeapOnly&) = delete; HeapOnly& operator=(const HeapOnly&) = delete; private: HeapOnly() {} ~HeapOnly() {} }; 这样,用户无法通过拷贝来创建对象,进一步确保只能堆上创建。 总结:通过私有构造函数和析构函数,静态工厂方法,以及禁用拷贝构造和赋值,可以实现只能堆上创建对象。 另外,引用[3]中的方法可能也是似的,所以最终的实现应该是这样的结构。</think>要设计一个C++使其对象只能堆上创建,核心思路是**限制对象的构造和析构**,具体实现步骤如下: --- ### 一、禁用上构造的3个关键步骤 1. **私有化析构函数** 当析构函数不可访问时,编译器会阻止对象的自动析构[^2]。例如: ```cpp class HeapOnly { private: ~HeapOnly() {} // 析构函数私有化 }; ``` 此时声明对象`HeapOnly obj;`会因无法调用析构函数而编译报错。 2. **提供静态工厂方法** 通过静态成员函数`create()`动态创建对象,并返回指针: ```cpp class HeapOnly { public: static HeapOnly* create() { return new HeapOnly(); // 调用私有构造函数 } private: HeapOnly() {} // 构造函数私有化 ~HeapOnly() {} }; ``` 用户只能通过`HeapOnly* obj = HeapOnly::create();`创建对象。 3. **禁用拷贝和赋值操作** 通过`= delete`禁止拷贝构造函数和赋值运算符,防止通过拷贝创建对象: ```cpp class HeapOnly { public: // ...其他代码 HeapOnly(const HeapOnly&) = delete; HeapOnly& operator=(const HeapOnly&) = delete; }; ``` --- ### 二、完整代码示例 ```cpp class HeapOnly { public: static HeapOnly* create() { return new HeapOnly(); } void destroy() { delete this; // 手动释放对象 } // 禁用拷贝和赋值 HeapOnly(const HeapOnly&) = delete; HeapOnly& operator=(const HeapOnly&) = delete; private: HeapOnly() = default; // 构造函数私有 ~HeapOnly() = default; // 析构函数私有 }; ``` --- ### 三、使用方法 1. **创建对象** ```cpp HeapOnly* obj = HeapOnly::create(); // 正确:堆上创建 ``` 2. **销毁对象** ```cpp obj->destroy(); // 正确:调用私有析构函数 ``` 3. **禁止的操作** ```cpp HeapOnly obj1; // 错误:构造函数私有 HeapOnly obj2(*obj); // 错误:拷贝构造函数被删除 HeapOnly* obj3 = new HeapOnly(); // 错误:构造函数私有 ``` --- ### 四、原理解析 1. **对象限制** 对象在生命周期结束时需要自动调用析构函数,而私有析构函数阻止了这一过程。 2. **对象控制** 通过静态工厂方法`create()`,将构造过程封装在内部,确保用户只能通过`new`创建对象[^3]。 3. **内存管理** 提供`destroy()`方法替代直接`delete`操作,解决私有析构函数导致的释放问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值