我在this博客中读到,将新运算符设为私有是一种在堆栈上实施实例化的好方法。
我正在实施一个使用RAII习语的课程。这个类显然只应该在堆栈上实例化,所以我正在寻找一种强制执行的方法。
感谢您的帮助!
修改
我的RAII类只是实例化我正在处理的框架的各个部分,因此除了在堆栈上创建实例之外,对该类执行任何其他操作都没有意义。
目标只是提供一个简单的可能性来配置框架并将其置于即用状态,而无需在客户端代码中实例化10个对象。
答案 0 :(得分:7)
这个班级显然应该只是 在堆栈上实例化,所以我 正在寻找一种强制执行的方法。
对于用户来说,如果你必须执行它,我想这对他们来说并不明显......
主要缺点是它不起作用。
即使新运算符是私有的,用户仍然可能会意外地将您的类用作其自己类的数据成员或基类,然后使用new
实例化其类。当他们写Foo *f = new Foo();
时,它会阻止他们写Foo f;
,但它并没有强制他们使用你的RAII类符合词法范围,这可能是你真正想要强制执行的
如果有人想要使用具有动态存储持续时间的锁(或其他),那么它们要么非常聪明,要么非常愚蠢。无论哪种方式,如果你让他们自己创造它,那么你无法阻止他们绕过你的限制。
你可以做的是让所有构造函数都是私有的(包括copy ctor),然后提供一个按值返回的友元函数。他们必须写:
const Foo &f = GimmeAFoo();
标准要求的将延长返回值的生命周期,直至f
的范围。由于它们无法复制对象,因此无法使值超出引用范围。由于它们无法初始化数据成员或基类,因此无法使用该变通方法。请注意,它们必须采用const引用,因此如果您的类需要非const成员,它就不会真正起作用。
他们仍然可以做一些蠢事,比如像这样初始化他们自己的类的const引用数据成员,但对象本身不会逃避发生这种情况的范围。他们将留下一个悬挂的参考,就像他们指出他们不应该做的东西一样。
答案 1 :(得分:2)
即使你无法阻止其他人将RAII类作为基类或动态创建的对象的成员,正如史蒂夫所说,并且你应该总是告诉用户你对这个类的预期用途,它仍然是个好主意。将新操作符设为私有以防止最明显的滥用。
因为这是所有有效的C ++代码,所以没有可移植性问题。
答案 2 :(得分:2)
我正在实施一个使用RAII习语的课程。这个类显然只应该在堆栈上实例化,所以我正在寻找一种强制执行的方法。
那不是RAII。 RAII在析构函数中构造和去初始化时正在初始化。 RAII是关于您控制的资源(例如数据成员)而不是“父”对象的生命周期。然后,您的类型的用户再次应用RAII来控制他们分配的生命周期。例如,您可以动态分配std :: vector,即使vector使用RAII来确保清除每个项目 it “ownns”。
我的问题是,这是否有任何不直接看到的副作用?
是的,你阻止其他方式有效(至少就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).
}
在堆栈上强制实例化是一种好方法吗?
你想阻止什么?用例是什么?任何使用新类型的人都知道他们正在做什么并且需要它,或者2)无论你做什么,都会严重破坏资源管理。你通过试图强制执行此操作来阻止#1中的那些人而不帮助#2中的那些人。
这是史蒂夫所说的另一种方式:
这个类显然只能在堆栈上实例化,所以我正在寻找一种强制执行的方法。
如果这是显而易见的,为什么需要强制执行?
如果用户盲目使用“new T()”,是什么让你觉得他们不会盲目地使用“:: new T()”?
目标只是提供一个简单的可能性来配置框架并将其置于即用状态,而无需在客户端代码中实例化10个对象。
#include <framework>
int main() {
framework::Init init;
// Do stuff.
}
在您的文档示例中突出显示使用此内容或与其非常接近的内容。