如何禁止构造物体?我将= 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{};
但x
,y
和*z
仍然可以存在。该怎么办?我对这两种情况都感兴趣;静态/堆栈分配和堆分配。
答案 0 :(得分:14)
答案 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)
如果您只想拥有static
个成员,请写下namespace A
而不是struct A
。继承代码在语法上是相似的。
要防止创建类的实例,请将其设为抽象。 (包括一个纯虚函数)。但这样做会在你的课堂上引入一个你可能不想要的v-table。
答案 3 :(得分:3)
通常,要完全阻止类的客户端代码实例化,您可以声明类final
和
使构造函数非 - public
或
删除构造函数并确保该类不是聚合,或
添加纯虚拟成员函数(例如,使析构函数为纯虚拟)以使类抽象化。
当非final
为public
时,以及对于抽象类,为了防止派生的基类子对象的实例化,必须声明类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 compiler与 friend
的事实是由于该编译器中的错误和/或语言扩展。
代码不应该编译,因为它调用已删除的默认构造函数。并且它不能用例如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在评论中解释的那样,用户提供的的要求是
“如果用户声明并且未明确指定,则特殊成员函数用户提供 在第一份声明中默认或删除。
即。虽然这个问题的例子中的默认构造函数的new
声明了构造函数,但它不是用户提供的(并且是其他成员的ditto),因此该类是聚合。
我能找到的关于这个措辞的唯一缺陷报告是DR 1355,但是这只涉及使用“特殊成员”这个词的问题,并建议放弃这些词。但是,考虑到这个问题所证明的效果,并考虑到只能在第一个声明中删除一个函数,措辞很奇怪。
总结一下,从C ++ 11开始(我还没有检查过C ++ 14),代码应该编译。但这可能是标准中的缺陷,措辞不能反映意图。由于MinGW g ++ 5.1.0没有编译代码,截至2015年10月依靠代码编译并不是一个好主意。
答案 4 :(得分:1)
本质上,这是编译并且是允许的,因为类型A
是聚合类型,并且聚合初始化不使用默认构造函数。
类类型(通常是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...
};