在构造函数中初始化

Initializing in Constructor

本文关键字:初始化 构造函数      更新时间:2023-10-16

我见过很多代码,其中编码人员为类定义了一个init((函数,并在创建实例后第一件事调用它。

在构造函数中进行所有初始化是否有任何危害或限制?

通常是为了可维护性,并在多个构造函数调用相同的初始化代码时减小代码大小:

class stuff 
{
public:
    stuff(int val1) { init(); setVal = val1; }
    stuff()         { init(); setVal = 0; }
    void init()     { startZero = 0; }
protected:
    int setVal;
    int startZero;
};

恰恰相反:通常最好将所有初始化在构造函数中。 在C++,"最佳"策略通常是将初始值设定项列表中的初始化,以便成员直接使用正确的值构造,而不是默认值构建,然后分配。 在 Java 中,您希望避免使用函数(除非是privatefinal(,因为动态分辨率可以把你进入一个尚未初始化的对象。

关于您使用init()函数的唯一原因是因为有很多具有重要共性的构造函数。 (在这种情况下C++,您仍然需要权衡默认值之间的差异施工,然后分配与立即施工正确的值。

在 Java 中,有充分的理由保持构造函数简短并将初始化逻辑移动到 init() 方法中:

  • 构造函数不是继承的,因此任何子类都必须重新实现它们或提供与super链链的存根
  • 不应在构造函数中调用可重写的方法,因为您可能会发现对象处于部分初始化的不一致状态

它是一种设计模式,与从对象构造函数内部引发的异常有关。

C++如果从对象构造函数内部抛出异常,则语言运行时将该对象视为根本不构造。因此,当对象超出范围时,不会调用对象析构函数。

这意味着,如果你的构造函数中有这样的代码:

int *p1 = new int;
int *p2 = new int;

并在析构函数中编写如下代码:

delete p1;
delete p2;

并且构造函数内部 P2 的初始化由于没有更多可用内存而失败,然后 new 运算符会引发bad_alloc异常。此时,即使 p1 的内存已正确分配,您的对象也没有完全构造。如果发生这种情况,析构函数将不会被调用,并且您正在泄漏 p1。

因此,在构造函数中放置的代码越多,发生错误的可能性就越大,从而导致潜在的内存泄漏。

这就是这种设计选择的主要原因,毕竟它并不太疯狂。

更多关于这一点的信息,请访问 Herb Sutter 的博客:C++ 中的构造函数例外

这是一个设计选择。 您希望使构造函数尽可能简单,以便轻松阅读它正在执行的操作。这就是为什么您经常会看到调用其他方法或函数的构造函数,具体取决于语言。它允许程序员阅读并遵循逻辑,而不会迷失在代码中。

随着构造函数的发展,您可以快速遇到一个场景,其中您希望触发大量事件。好的设计要求你把这些序列分解成更简单的方法,再次,使其更具可读性,将来更易于维护。

所以,不,没有伤害或限制,这是一种设计偏好。如果您需要在构造函数中完成所有初始化,请在那里进行。如果您只需要稍后完成它,请将其放入稍后调用的方法中。无论哪种方式,这完全取决于您,并且没有硬性或快速规则。

如果您有同一类或不同类的多个对象需要使用指向彼此的指针进行初始化,以便至少有一个指针依赖循环,则无法单独在构造函数中执行所有初始化。(当另一个对象尚未创建时,您将如何使用指向另一个对象的指针/引用构造第一个对象?

这种情况很容易发生的一种情况是,在事件模拟系统中,不同的组件进行交互,因此每个组件都需要指向其他组件的指针。

由于不可能在构造函数中进行所有初始化,因此至少必须在 init 函数中进行一些初始化。这就引出了一个问题:哪些部分应该在 init 函数中完成?灵活的选择似乎是在 init 函数中执行所有指针初始化。然后,您可以按任何顺序构造对象,因为在构造给定对象时,您不必担心是否已经拥有指向它需要了解的其他对象的必要指针。