【问题标题】:C++, preventing class instance from being created on the stack (during compiltaion)C++,防止在堆栈上创建类实例(编译期间)
【发布时间】:2010-06-22 10:25:45
【问题描述】:

我知道有一些方法可以阻止在堆上创建类,方法是阻止用户使用new 和delete 运算符。我正试图做相反的事情。我有一个类,我想阻止用户在堆栈上创建它的实例,并且只有使用 new 运算符发起的实例才会编译。更具体地说,我希望以下代码在编译期间收到错误:

MyClass c1; //compilation error

MyClass* c1 = new MyClass(); //compiles okay

通过搜索网络,我找到了有关如何操作的建议:

class MyClass {
public:
    MyClass();
private:
    void destroy() const { delete this; }

...

private:
    ~MyClass();
};

int main(int argc,char** argv)
{
    MyClass myclass; // <--- error, private destructor called here !!!

    MyClass* myclass_ptr = new MyClass;
    myclass_ptr->destroy();
}

我不明白为什么这应该有效。为什么在创建MyClass 的实例时会调用析构函数?

【问题讨论】:

  • 顺便说一句,像这样调用 destrory() 也会给你一个编译器错误,因为它被声明为私有
  • 我很想知道你为什么想要这个?
  • 查看我的另一个问题,它彻底解释了它:stackoverflow.com/questions/3095856/…

标签: c++ class-design


【解决方案1】:

当myclass 到达其作用域的末尾(下一个})时,编译器调用析构函数将其从堆栈中释放。但是,如果析构函数是私有的,则无法访问析构函数,因此无法将类放入堆栈。

我不喜欢delete this 的样子。一般来说,我认为物体不应该自我毁灭。也许更好的方法是为您的类设置一个私有的构造函数,然后使用静态函数创建一个实例。

// In class declaration...
static MyClass* Create()
{
    return new MyClass(); // can access private constructor
}

// ...

MyClass myclass; // illegal, cannot access private constructor

MyClass* pMyClass = MyClass::Create();
delete pMyClass; // after usage

【讨论】:

  • +1 - 而不是使用单例模式的变体来搞乱私有析构函数是要走的路
  • @Holger: 单例的唯一目的是允许只有一个实例,而这并没有这样做,所以它不是单例完全没有。 我希望我能对 cme​​ts 投反对票。
  • +1 很好,使用工厂方法而不是破解对不得不使用您的代码的人来说可能不那么明显的东西。
  • +1 在大多数自动指针上会比原来的更好。
  • @sbi:我希望我也能做到。
【解决方案2】:

为什么在创建MyClass 的实例时会调用析构函数?

不是。但是,当实例超出范围时必须自动调用它。如果它是私有的,则编译器不得生成该代码,因此会出现错误。

如果您认为将析构函数设为私有是晦涩难懂的,那么将类限制为动态分配的另一种方法是将所有构造函数设为私有,并且只有 MyClass::create() 函数返回动态分配的对象:

class MyClass {
public:
  static MyClass* create()             {return new MyClass();}
  static MyClass* create(const Foo& f) {return new MyClass(f);}
private:
  MyClass();
  MyClass(const Foo&);
};

请注意,返回指向必须删除的对象的裸指针是不受欢迎的。您应该返回智能指针:

class MyClass {
public:
  static std::shared_ptr<MyClass> create()             {return new MyClass();}
  static std::shared_ptr<MyClass> create(const Foo& f) {return new MyClass(f);}
  // ...
};

【讨论】:

  • -1 表示“如果它是私有的,则编译器不得生成该代码,...”
  • 如果是私有的,只能在类范围内调用。这并不意味着编译器必须或不能生成代码。
  • @Berkus:隐私只是编译器前端的一项功能。除了编码到其中不允许访问的规则之外,没有什么会阻碍编译器生成访问私有数据的代码。你可以在 #define private public 包含一个只作为目标文件(通常在 lib 中)的类之前看到这一点:虽然在技术上调用未定义的行为,但大多数编译器会很乐意生成访问该类中私有数据的代码。从技术上讲,这不是问题,所以编译器不允许访问私有的唯一原因是因为它们不能。
  • @Berkus:“如果它被声明为私有,则编译器不得生成该代码。”是绝对正确的,尽管过于具体。更好的是,“如果它被声明,编译器就不能生成该代码,程序员声称自己有权编写它。” C++0x 用默认的成员函数解决了这个问题。
  • 谢谢 sbi。我们对所有共享对象都使用智能指针,但这绝对值得注意。
【解决方案3】:

因为当实例超出范围时,必须使用析构函数对其进行破坏。指向实例的指针不这样做。

【讨论】:

    【解决方案4】:

    只要局部变量超出范围,它就会被销毁。 在销毁时,会调用对象的析构函数。在这里,范围是主要功能。当程序退出时,会调用myclass对象的析构函数

    【讨论】:

      【解决方案5】:

      不是。编译器试图在析构函数超出范围时调用它,并指示产生这种效果的代码行,这比指向函数末尾有用得多。

      【讨论】:

      • 这应该是一个评论(你根本不回答(主要)问题)。上面的 Pardeep 也是如此。
      • 问题是“为什么要在创建 MyClass 的实例时调用析构函数?”。这是对那个问题的回答。先学会阅读。
      • @Berkus 嗯?标题中的问题是“C++,防止在堆栈上创建类实例(在编译期间)”。抱歉,这不回答这个问题。
      • 问题的标题不是问题本身。问题是以问号结尾的句子或段落。
      猜你喜欢
      • 2011-12-16
      • 2019-07-27
      • 2012-11-23
      • 2017-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多