如何禁止对象的构造?

How to prohibit the construction of object?

如何禁止对象的构造?我标记= 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仍然可以存在。该怎么办?我对这两种情况都感兴趣; static/stack分配和堆分配。

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

  2. 要防止创建 class 的实例,请将其抽象化。 (包括一个纯虚函数)。但是这样做会给你引入一个 v-table class,你可能不想要它。

一个选择是给 class 一个纯虚函数,并将其标记为最终的:

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

[Live example]

如果你想让 class 无法实例化,你可以只声明私有构造函数:

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.
    }
};

一般来说,要完全阻止 class 的客户端代码实例化,您可以声明 class final

  • 使构造函数非public,或

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

  • 添加一个纯虚成员函数(例如,使析构函数成为纯虚函数)使 class 抽象。

当非 publicprotected 时,声明 class final 是必要的,对于抽象 class,为了防止实例化派生 class.

的基础 class 子对象

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

  • 使析构函数成为非public.

这会阻止自动变量和静态变量,但不会阻止 new 的动态分配。

  • 使class'分配函数(operator new)非public.

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

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

friend

代码 compiles with at least one version of the clang compiler-std=gnu++1z 的事实是由于该编译器中的错误 and/or 语言扩展。

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

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

  • 尽管代码可以使用 clang 和 Visual C++ 2015 进行编译,但不能使用例如MinGW g++ 5.1.0,即使 -std=gnu++1z.

  • 直觉上,如果代码应该编译,delete 将毫无意义,但在 C++ 中允许许多无意义的构造。

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

C++11 §8.4.2/4

A special member function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

即尽管此问题示例中默认构造函数的 delete 声明了该构造函数,但它不是用户提供的(其他成员也是如此),因此 class 是一个聚合。

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

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

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

What is an aggregate type?;

class type (typically, struct or union), that has

  • no private or protected members
  • no user-provided constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)
  • no base classes
  • no virtual member functions

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

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 "trick" 声明构造函数对 "hide" 私有,因此无法使用。我希望默认构造函数上的 =delete 有效地禁止 class 创建(在其他用户定义的构造函数之外)。


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

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

也许最简单的方法是 return 这里采用 C++03 风格,将默认构造函数设为私有;

struct A
{
private:
    A(); // note no =delete...
};