如何禁止构造对象?

时间:2015-10-15 10:28:13

标签: c++ c++11 memory-management c++14 memory-layout

如何禁止构造物体?我将= delete;所有相关的特殊功能标记如下:

struct A
{
    A() = delete;
    A(A const &) = delete;
    A(A &&) = delete;
    void * operator new(std::size_t) = delete;
    void operator delete(void *) = delete;
};
A x{};
A y = {};
A * z = ::new A{};

LIVE EXAMPLE

xy*z仍然可以存在。该怎么办?我对这两种情况都感兴趣;静态/堆栈分配和堆分配。

5 个答案:

答案 0 :(得分:14)

一种选择是为类提供纯虚函数,并将其标记为final:

struct A final
{
  virtual void nonconstructible() = 0;
};

[Live example]

答案 1 :(得分:6)

如果您想要实例化类,则可以声明私有构造函数:

class NotInstantiable {
private:
    NotInstatiable();

public:
};

并没有进一步定义NotInstantiable。现在无法实例化,因为首先构造函数是private,但是还没有提供构造函数的定义。

实例化NotInstantiable的第二个障碍例如会禁止这种可能性,实际上这是另一种众所周知的模式:

class NotInstantiable {
private:
    NotInstantiable();

public:
    NotInstantiable* evil_method()
    {
        return new NotInstantiable(); // this will fail if there's no body of the constructor.
    }
};

答案 2 :(得分:5)

  1. 如果您只想拥有static个成员,请写下namespace A而不是struct A。继承代码在语法上是相似的。

  2. 要防止创建类的实例,请将其设为抽象。 (包括一个纯虚函数)。但这样做会在你的课堂上引入一个你可能不想要的v-table。

答案 3 :(得分:3)

通常,要完全阻止类的客户端代码实例化,您可以声明类final

  • 使构造函数非 - public

  • 删除构造函数并确保该类不是聚合,或

  • 添加纯虚拟成员函数(例如,使析构函数为纯虚拟)以使类抽象化。

当非finalpublic时,以及对于抽象类,为了防止派生的基类子对象的实例化,必须声明类protected类。

要部分禁止实例化,您可以

  • 使析构函数非 - public

这会阻止自动和静态变量,但不会阻止使用new进行动态分配。

  • 使类的分配功能(operator new)非 - public

这可以防止通过客户端代码中的普通new - 表达式进行动态分配,但它不提供自动和静态变量或其他对象的子对象,并且它不会阻止通过{{ 1}} - 表达式,它使用全局分配函数。

还有其他相关技术,例如带有额外参数的分配函数,使::new - 表达式过于复杂和不切实际。我用过一次强制使用特殊的宏动态分配对象,例如对于shared-from-this class。但那是在C ++ 11支持转发参数之前的时间;现在一个普通的函数可以完成这项工作,这样的函数可以成为该类的new

代码compiles with at least one version of the clang compilerfriend的事实是由于该编译器中的错误和/或语言扩展。

代码不应该编译,因为它调用已删除的默认构造函数。并且它不能用例如MinGW g ++ 5.1.0,即使是-std=gnu++1z

代码compiles with at least one version of the clang compiler-std=gnu++1z的事实可能是由于该编译器中的错误和/或语言扩展。正确的行为是什么,不清楚因为

  • 虽然代码使用clang和Visual C ++ 2015编译,但它不能用例如MinGW g ++ 5.1.0,即使是-std=gnu++1z

  • 直观地,如果代码应该编译,-std=gnu++1z将毫无意义,但在C ++中允许使用许多无意义的构造。

  • 问题是该类是否是聚合(在这种情况下delete表达式执行聚合初始化),这取决于是否可以将删除的默认构造函数视为用户提供的。正如用户TartanLlama在评论中解释的那样,用户提供的的要求是

C ++11§8.4.2/ 4
  

如果用户声明并且未明确指定,则特殊成员函数用户提供   在第一份声明中默认或删除。

即。虽然这个问题的例子中的默认构造函数的new声明了构造函数,但它不是用户提供的(并且是其他成员的ditto),因此该类是聚合。

我能找到的关于这个措辞的唯一缺陷报告是DR 1355,但是这只涉及使用“特殊成员”这个词的问题,并建议放弃这些词。但是,考虑到这个问题所证明的效果,并考虑到只能在第一个声明中删除一个函数,措辞很奇怪。

总结一下,从C ++ 11开始(我还没有检查过C ++ 14),代码应该编译。但这可能是标准中的缺陷,措辞不能反映意图。由于MinGW g ++ 5.1.0没有编译代码,截至2015年10月依靠代码编译并不是一个好主意。

答案 4 :(得分:1)

本质上,这是编译并且是允许的,因为类型A是聚合类型,并且聚合初始化不使用默认构造函数。

What is an aggregate type?;

  

类类型(通常是struct或union),具有

     
      
  • 没有私人或受保护的会员
  •   
  • 没有用户提供的构造函数(允许显式默认或删除构造函数)(自C ++ 11起)
  •   
  • 没有基类
  •   
  • 没有虚拟成员函数
  •   

给它上述任何一个都会使它成为非聚合的,因此聚合初始化将不适用。给它一个私有用户定义(和未实现)的构造函数就可以了。

struct A
{
    A() = delete;
    A(A const &) = delete;
    A(A &&) = delete;
    void * operator new(std::size_t) = delete;
    void operator delete(void *) = delete;
private:
    A(int);
};

作为旁注;我希望这是语言规范的缺陷。初看起来我认为这不应该编译,但确实如此。 =delete的一个动机是避免C ++ 03"技巧"声明构造函数是私有的" hide"它们因而无法使用。我希望默认构造函数上的=delete能够有效地禁止创建类(在其他用户定义的构造函数之外)。

为了更容易阅读和更清晰的意图,甚至考虑一个空的基类;

struct NonAggregate{};
struct A : private NonAggregate
{
    //...

也许最简单的是在这里返回C ++ 03样式,将默认构造函数设为私有;

struct A
{
private:
    A(); // note no =delete...
};
相关问题