避免在构造函数中分配或保持简单性(和 RAII?)
Avoid allocating in constructor or preserve simplicity (and RAII?)
我已经将一个游戏引擎作为一个项目放在一起,以了解更多关于 c++ 的信息大约 8 个月了。我已经到了一个阶段,为了避免在构造函数中进行分配(根据大学讲师的建议),我为我的所有对象设置了虚拟 setup() 方法,并使用 bool 标记设置方法是否已被调用。但是,我遇到了各种逻辑错误,而且由于在设置中进行了分配,因此无法在构造函数中执行任何操作是一个巨大的痛苦。我也一直在阅读有关 RAII 的内容,似乎只在构造函数中进行分配可能会更好,这样我就不必调用设置方法。
在构造函数中通过 new 分配内存有多糟糕?我应该这样做以符合 RAII 吗?
编辑 - 澄清一下,人们指出使用 std::vector 容器等。我说的是为更多对象分配内存,而不是为数组和其他东西分配内存。例如 - 一个 Button 对象需要为自己创建一个 TransformComponent 、一个 AnimationComponent 和一个 RenderComponent。目前,在 setup() 中我会使用 new 创建它们。使用智能指针等是否意味着我不需要 new 关键字?
我正在创建的对象被转发到名为 addComponent(Component * ) 的基础 Class 方法,该方法会将此组件存储在 Component * 的 std::vector 中,所以我可以'在 method/constructor.
结束时没有清理这些对象
我的印象是
ButtonClass()
{
SomeComponent * sc = new SomeComponent ();
addComponent(sc);
}
工作正常,但是
ButtonClass()
{
SomeComponent sc = SomeComponent ();
addComponent(&sc);
}
会导致 sc 被清理,传递给 addComponent 的对它的引用将是指向空内存的指针。
如果使用智能指针否定了这一点,那么我误解了他们,我认为他们只是以一种比调用 new 然后删除更简洁的方式为我删除了东西。
我被告知 "Never allocate in a constructor" 的原因是,如果构造函数失败,您将无法恢复该内存。但是同意下面的评论,我一直怀疑如果发生这种情况我无论如何都会终止....
如果您有许多构造函数做几乎相同的事情,这种 init() 或 setup() 方法就很好。
但我认为这本身就是一个设计错误。
所以,我建议再次阅读您讲师的建议,如果它对您仍然意味着相同,那么请阅读其他人的意见,例如 Scott Meyers。
构造函数是分配资源的自然场所。我见过读取完整数据库以设置应用程序的构造函数。
使用智能指针分配事物,然后您就可以免去照看它们的责任,因为它们足够聪明,可以照顾自己。
玩得开心!
一个好的经验法则是每个 class 应该有一个责任。我们已经有一些(模板)classes 负责处理堆分配的内存,例如 std::vector
、std::string
和 std::unique_ptr
。大多数时候,您应该使用它们来避免给您的 class 额外的内存处理责任。
现在您可能需要一种标准 class 不提供的特定类型的内存处理。或者更常见的是,您还有其他一些也需要清理处理的非内存资源——例如一个临时文件。在这些情况下,"one class, one responsibility" 原则仍然成立。您将每个此类资源包装在其自己的资源管理中 class。需要三种资源的更复杂的对象只有三个成员,每个成员处理一种资源。
C++ 现在可以确保即使出现异常,资源也不会丢失。即使在某个复杂对象的构造函数中间无法创建一个成员,C++ 也会安排销毁到目前为止创建的所有成员,而且只有那些。这为您提供了一种全有或全无的方法。没有半成品。
要特别清楚:这仅适用于构造函数。它不适用于您自己的 setup()
。换句话说,C++ 有特定的规则来使资源获取在构造函数内部工作,并且只有。这就是术语 "Resource Acquisition is Initialisation" 或 RAII 的原因。如果您的讲师建议 反对 构造函数中的资源分配,那么他们根本不了解 C++。
我已经将一个游戏引擎作为一个项目放在一起,以了解更多关于 c++ 的信息大约 8 个月了。我已经到了一个阶段,为了避免在构造函数中进行分配(根据大学讲师的建议),我为我的所有对象设置了虚拟 setup() 方法,并使用 bool 标记设置方法是否已被调用。但是,我遇到了各种逻辑错误,而且由于在设置中进行了分配,因此无法在构造函数中执行任何操作是一个巨大的痛苦。我也一直在阅读有关 RAII 的内容,似乎只在构造函数中进行分配可能会更好,这样我就不必调用设置方法。
在构造函数中通过 new 分配内存有多糟糕?我应该这样做以符合 RAII 吗?
编辑 - 澄清一下,人们指出使用 std::vector 容器等。我说的是为更多对象分配内存,而不是为数组和其他东西分配内存。例如 - 一个 Button 对象需要为自己创建一个 TransformComponent 、一个 AnimationComponent 和一个 RenderComponent。目前,在 setup() 中我会使用 new 创建它们。使用智能指针等是否意味着我不需要 new 关键字?
我正在创建的对象被转发到名为 addComponent(Component * ) 的基础 Class 方法,该方法会将此组件存储在 Component * 的 std::vector 中,所以我可以'在 method/constructor.
结束时没有清理这些对象我的印象是
ButtonClass()
{
SomeComponent * sc = new SomeComponent ();
addComponent(sc);
}
工作正常,但是
ButtonClass()
{
SomeComponent sc = SomeComponent ();
addComponent(&sc);
}
会导致 sc 被清理,传递给 addComponent 的对它的引用将是指向空内存的指针。
如果使用智能指针否定了这一点,那么我误解了他们,我认为他们只是以一种比调用 new 然后删除更简洁的方式为我删除了东西。
我被告知 "Never allocate in a constructor" 的原因是,如果构造函数失败,您将无法恢复该内存。但是同意下面的评论,我一直怀疑如果发生这种情况我无论如何都会终止....
如果您有许多构造函数做几乎相同的事情,这种 init() 或 setup() 方法就很好。
但我认为这本身就是一个设计错误。
所以,我建议再次阅读您讲师的建议,如果它对您仍然意味着相同,那么请阅读其他人的意见,例如 Scott Meyers。
构造函数是分配资源的自然场所。我见过读取完整数据库以设置应用程序的构造函数。
使用智能指针分配事物,然后您就可以免去照看它们的责任,因为它们足够聪明,可以照顾自己。
玩得开心!
一个好的经验法则是每个 class 应该有一个责任。我们已经有一些(模板)classes 负责处理堆分配的内存,例如 std::vector
、std::string
和 std::unique_ptr
。大多数时候,您应该使用它们来避免给您的 class 额外的内存处理责任。
现在您可能需要一种标准 class 不提供的特定类型的内存处理。或者更常见的是,您还有其他一些也需要清理处理的非内存资源——例如一个临时文件。在这些情况下,"one class, one responsibility" 原则仍然成立。您将每个此类资源包装在其自己的资源管理中 class。需要三种资源的更复杂的对象只有三个成员,每个成员处理一种资源。
C++ 现在可以确保即使出现异常,资源也不会丢失。即使在某个复杂对象的构造函数中间无法创建一个成员,C++ 也会安排销毁到目前为止创建的所有成员,而且只有那些。这为您提供了一种全有或全无的方法。没有半成品。
要特别清楚:这仅适用于构造函数。它不适用于您自己的 setup()
。换句话说,C++ 有特定的规则来使资源获取在构造函数内部工作,并且只有。这就是术语 "Resource Acquisition is Initialisation" 或 RAII 的原因。如果您的讲师建议 反对 构造函数中的资源分配,那么他们根本不了解 C++。