C++禁止堆栈实例但允许new和delete。

4
基本上我想要的是:
class MyClass{
    public:
        MyClass() = default;
    // what should I do?
}

MyClass mc; // compile time error;
auto pmc = new MyClass; //OK
delete pmc; //OK too

我知道可以通过隐藏构造函数(现在不能在类外部使用new)或隐藏析构函数(现在不能在类外部使用delete)或同时隐藏两者,使其只能通过堆进行操作。但如果我不想引入新的命名函数,只想使用老旧的new和delete,有没有可能实现呢?即使通过一些技巧也可以吗?


4
不,只需编写一个函数。 - Benjamin Lindley
编辑您的问题,解释为什么要禁止MyClass的栈自动变量,并给出一个更有动机的例子。 - Basile Starynkevitch
我希望 new 运算符可以以一种可重载的方式进行重载,使其能够返回类的实例,并且可以像构造函数一样接受任意数量的参数,这样我们就可以编写类似于 auto px = new X(arg0, arg2, arg3); 的代码了。而 delete 则可以像析构函数一样使用。 - Nawaz
3个回答

6
我的“类智能指针但不完全相同”的想法:
#include <iostream>

class MyClass_ {
  private:
    /**/     MyClass_( void ) { }
    /**/    ~MyClass_( void ) { }
  public:
    void     func( void ) const { std::cout << "Hello" << std::endl; }

    friend class MyClass;
} ;

class MyClass {
  public:
    /**/     MyClass( void ) : p( new MyClass_ ) { }
    /**/    ~MyClass( void ) { delete p; }

    // Tricky implementation details follow...
    // The question in all cases is, who owns the MyClass_ that has been
    // allocated on the heap?  Do you always allocate a new one, and then
    // copy the guts?  (That might be expensive!)  Do you change ownership?
    // Then what about the other MyClass?  What does it point to?
    // Or do you share ownership?  Then you need to ref-count so you don't
    // delete too soon.  (And this whole thing turns into an ordinary
    // shared_ptr<MyClass_>)

    /**/     MyClass( const MyClass &o ) { }
    /**/     MyClass( MyClass &&o ) { }
    MyClass &operator=( const MyClass &o ) { }
    MyClass &operator=( MyClass &&o ) { }

    MyClass_ * operator->( void ) { return p; }
    const MyClass_ * operator->( void ) const { return p; }

  private:
    MyClass_ *p;
} ;

int
main( int, char ** )
{
    MyClass  a;                  // this will be destroyed properly
    MyClass *b = new MyClass;    // this will leak if you don't delete it

    a->func( );
    (*b)->func( );

    return 0;
}

3
这很巧妙。它说明了类只能存在于堆上这一事实实际上并不需要用户知道。他们关心的只是实例化一个对象。通过将堆分配隐藏为实现细节,如果需要,您还可以为包装对象保留值语义。 - ComicSansMS
1
正确地实现五个规则,或者包括一个需要执行该操作的注释(或删除它们)。否则,在复制对象时将得到未定义的行为。 - Rakete1111
1
^那个。如果你不打算写它们,至少提一下它们。 - Passer By
抱歉,睡觉前我完全没有想到这件事。我对c++-xxx不是很了解,但让我看看我能做些什么。 - Dave M.
在这种情况下,实际上并不那么复杂。如果他们只想要移动语义(使用unique_ptr成员)或引用语义(使用shared_ptr成员),那么根本不需要考虑五法则。只有当他们需要值语义时,才需要考虑这些特殊成员函数(即使在这些情况下,通过实现value_ptr也可以抽象掉它。当然,负担仍然存在)。 - J. Doe

0
这可能听起来不是您想要的,但是可以将其包装在另一个类中。这样,您可以强制执行从堆分配存储空间,并将此类细节与API用户隔离开来。

你能提供代码和更清晰的解释吗?通过“surrounding”,你是指将其作为嵌套类吗?这样做有什么帮助呢? - Phil1970

0
通常的方法是将构造函数设置为private,并添加一些static成员函数(您可以称之为工厂或制造函数),该函数返回一个指针。
因此,您的类将如下所示:
class MyClass{
    private:
        MyClass() = default;
    public:
        static MyClass* make() { return new MyClass;  };
    // what should I do?
}

然后你就可以编写代码了:

 auto mc = MyClass::make();

在其他地方(而不是new MyClass

等等。但要注意五个规则,并考虑使用(作为您的MyClass :: make的返回类型)来自{{link3:<memory>头文件}}的一些智能指针

您还可以定义自己的智能指针类,具有自己的一元operator ->operator *以及受{{link5:std :: make_shared}}启发的可变参数模板...

只想要好旧的new和delete

在真正的C++11中,这被认为是不好的风格,可能会被视为不良风格。你应该避免在库之外明确使用new,并采用一些智能指针编码方式。

你可以使用智能指针的思想,但不是对对象进行引用计数,而是仅使用指针来确保所有底层对象都分配在堆上。(指针本身可以是分配的或自动的,这并不太重要。) - Dave M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接