5

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

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

4

6 回答 6

2

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

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

    void init()     { startZero = 0; }

protected:
    int setVal;
    int startZero;
};
于 2012-05-08T07:15:40.963 回答
2

恰恰相反:通常最好将所有初始化都放在构造函数中。在 C++ 中,“最佳”策略通常是将初始化放在初始化列表中,以便使用正确的值直接构造成员,而不是默认构造,然后分配。在 Java 中,您希望避免使用函数(除非它是privatefinal),因为动态解析可能会将您置于尚未初始化的对象中。

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

于 2012-05-08T08:04:49.733 回答
1

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

  • 构造函数不是继承的,因此任何子类都必须重新实现它们或提供链接的存根super
  • 您不应该在构造函数中调用可覆盖的方法,因为您会发现对象处于部分初始化的不一致状态
于 2012-05-08T07:17:50.360 回答
0

这是一个设计选择。你想让你的构造函数尽可能简单,所以很容易阅读它在做什么。这就是为什么您经常会看到调用其他方法或函数的构造函数,具体取决于语言。它允许程序员阅读和遵循逻辑,而不会迷失在代码中。

在构造函数中,您可能会很快遇到一个场景,您希望触发大量事件。好的设计要求您将这些序列分解为更简单的方法,以使其更具可读性并且在将来更易于维护。

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

于 2012-05-08T07:14:59.457 回答
0

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

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

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

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

和你的析构函数中这样的代码:

delete p1;
delete p2;

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

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

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

在 Herb Sutter 的博客上了解更多信息:C++ 中的构造函数异常

于 2012-05-08T08:27:39.440 回答
0

如果你有多个同一个类或不同类的对象需要用指针相互初始化,这样至少有一个循环的指针依赖,你不能单独在构造函数中完成所有的初始化。(当另一个对象尚未创建时,您将如何使用指向另一个对象的指针/引用来构造第一个对象?)

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

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

于 2012-12-07T19:02:24.560 回答