避免在构造函数中分配或保持简单性(和 RAII?

Avoid allocating in constructor or preserve simplicity (and RAII?)

本文关键字:简单 RAII 构造函数 分配      更新时间:2023-10-16

我已经把一个游戏引擎作为一个项目来学习更多关于 c++ 大约 8 个月了。我已经到了这样一个阶段,为了避免分配构造函数(在大学讲师的建议下(,我为所有对象提供了虚拟 setup(( 方法,以及一个布尔值来标记设置方法是否已被调用。但是,我遇到了各种逻辑错误,由于在设置中进行了分配,因此无法在构造函数中执行任何操作是一个巨大的痛苦。我也一直在阅读有关 RAII 的信息,似乎最好只在构造函数中分配,这样我就不必调用设置方法。

通过构造函数中的 new 分配内存有多糟糕?我应该这样做以符合 RAII 吗?

编辑 - 只是为了澄清,人们正在使用std::vector容器等指出。我说的是为更多对象分配内存,而不是为数组和事物分配内存。例如 - 按钮对象需要为自己创建一个转换组件、一个动画组件和一个渲染组件。目前,在 setup(( 中,我将使用 new 创建它们。使用智能指针等是否意味着我不需要新关键字?

我正在创建的对象被转发到一个名为addComponent(组件*(的基类方法,该方法将此组件存储在组件* s的std::vector中,因此我无法在方法/构造函数的末尾清理这些对象。

我的印象是

ButtonClass()
{
SomeComponent * sc = new SomeComponent ();
addComponent(sc);
}

工作正常,但是

ButtonClass()
{
SomeComponent sc = SomeComponent ();
addComponent(&sc);
}

将导致 sc 被清理,并且传递给 addComponent 的对它的引用将是指向空内存的指针。

如果使用智能指针否定了这一点,那么我误解了他们,我认为他们只是以比调用new然后删除更整洁的方式为我删除了东西。

我得到"从不分配构造函数"的原因是,如果构造函数失败,则无法恢复该内存。但是同意下面的评论,我一直怀疑如果发生这种情况,我无论如何都会终止......

一个好的经验法则是每个类都应该有一个责任。我们已经有一些(模板(类负责处理堆分配的内存,例如std::vectorstd::stringstd::unique_ptr。大多数情况下,您应该使用这些来避免给类额外的内存处理责任。

现在,您可能需要标准类未提供的特定类型的内存处理。或者更常见的是,您有一些其他非内存资源也需要清理处理 - 例如临时文件。在这些情况下,"一个阶级,一个责任"的原则仍然适用。将每个此类资源包装在其自己的资源管理类中。需要三个资源的更复杂的对象只有三个成员,每个成员处理一个资源。

C++现在确保即使出现异常,资源也不会丢失。即使在某个复杂对象的构造函数中间无法创建一个成员,C++也会安排到目前为止创建的所有成员都被销毁,并且只销毁这些成员。这为您提供了一种全有或全无的方法。没有半途而废的对象。

需要特别明确的是:这仅适用于构造函数。它不适用于您自己的setup()。换句话说,C++有特定的规则来使资源获取在构造函数中工作,并且只在那里工作。这就是为什么术语是"资源获取是初始化"或RAII。您的讲师从根本上不理解C++如果他们建议不要在构造函数中分配资源。

如果你有很多构造函数做几乎同样的事情,这种 init(( 或 setup(( 方式是很好的。

但我认为,这本身就是一个设计错误。

所以,我建议再次阅读你的讲师建议,如果它对你来说仍然意味着同样,那么阅读其他人的意见,比如斯科特迈耶斯。

构造函数分配资源的自然位置。我见过读取完整数据库以设置应用程序的构造函数。

使用智能指针来分配东西,然后你就免除了关注它们的职责,因为它们足够聪明,可以照顾好自己。

玩得开心!