私人新运营商是否有任何意想不到的副作用



我在这个博客中读到,将新运算符设为私有是在堆栈上强制执行实例化的好方法。

我正在实现一个使用 RAII 习语的类。这个类显然应该只在堆栈上实例化,所以我正在寻找一种方法来强制执行它。

  • 我的问题是,这是否有任何不直接看到的副作用?
  • 在堆栈上强制执行实例化是否是一种好方法?
  • 是否存在任何可移植性问题?

感谢您的帮助!

编辑

我的 RAII 类只是实例化我正在处理的框架的各个部分,因此除了在堆栈上创建实例之外,对该类执行任何其他操作都没有意义。

目标只是提供一种简单的可能性来配置框架并将其置于即用型状态,而无需在客户端代码中实例化 10 个对象。

这个类显然应该只是 在堆栈上实例化,所以我是 寻找一种方法来强制执行这一点。

我想这对用户来说并不明显,如果你必须强制执行它......

主要缺点是它不起作用

即使新运算符是私有的,用户仍然可以意外地将您的类用作数据成员或自己类的基类,然后使用 new 实例化其类。它阻止他们在应该写Foo f;时编写Foo *f = new Foo();,但它并没有强制他们对您的 RAII 类的使用与词法范围相匹配,这可能是您真正想要强制执行的。

如果有人想使用具有动态存储持续时间的锁(或其他任何东西),那么他们要么非常聪明,要么非常愚蠢。无论哪种方式,如果您让他们自己创建它,那么您都无法阻止他们绕过您的限制。

你可以做的是将所有构造函数设为私有(包括复制 ctor),然后提供一个按值返回的友元函数。他们必须写:

const Foo &f = GimmeAFoo();

标准要求将延长返回值的生命周期,直至f范围。由于它们无法复制对象,因此无法使值转义引用的范围。由于它们无法初始化数据成员或基类,因此无法使用解决方法。请注意,它们必须采用常量引用,因此如果您的类需要非常量成员,它实际上不起作用。

他们仍然可以做一些愚蠢的事情,比如像这样初始化自己类的 const 引用数据成员,但对象本身不会逃脱发生这种情况的范围。他们会留下一个悬而未决的引用,就像他们把指针指向他们不应该做的事情一样。

我正在实现一个使用 RAII 习语的类。这个类显然应该只在堆栈上实例化,所以我正在寻找一种方法来强制执行它。

那不是RAII。 RAII 在析构函数中构造和取消初始化时正在初始化。 RAII 是关于您控制的资源(例如数据成员),而不是"父"对象的生存期。 然后,您类型的用户再次应用 RAII 来控制他们分配的内容的生存期。 例如,您可以动态分配 std::vector,即使 vector 使用 RAII 来确保"拥有"的每个项目都被清理干净。


我的问题是,这是否有任何不直接看到的副作用?

是的,您阻止了其他有效(至少就 RAII 而言)使用您的类型。

void example() {
  shared_ptr<T> p (new T());
  // No RAII violation, still uses operator new.
  vector<T> v (some_initial_size);
  // No RAII violation, still uses operator new (the placement form is used
  // inside the default allocator).
}

在堆栈上强制执行实例化是否是一种好方法?

你想防止什么? 用例是什么? 任何使用new的人都可以1)知道他们在做什么并需要它,或者2)无论你做什么,都会严重搞砸资源管理。 您阻碍了 #1 中的人,而没有通过尝试强制执行来帮助 #2 中的人。

这是陈述史蒂夫所说的话的另一种方式:

这个类显然应该只在堆栈上实例化,所以我正在寻找一种方法来强制执行它。

如果这是显而易见的,为什么需要强制执行?

如果用户会盲目使用"new T()",是什么让你认为他们不会盲目使用"::new T()"?


目标只是提供一种简单的可能性来配置框架并将其置于即用型状态,而无需在客户端代码中实例化 10 个对象。

#include <framework>
int main() {
  framework::Init init;
  // Do stuff.
}

在文档示例中突出显示这个或非常接近它的东西。

即使你不能阻止其他人将RAII类作为基类或动态创建的对象的成员,正如Steve所说,并且你应该始终告知用户你对该类的预期用途,将新运算符设为私有以防止最明显的滥用仍然是一个好主意。

由于这些都是有效的C++代码,因此不存在可移植性问题。

相关内容

最新更新