应该在构造函数中执行需要一定时间的操作还是先构造对象再稍后初始化呢?
例如,在构造表示目录结构的对象时,对象及其子文件夹的填充应在构造函数中完成。显然,一个目录可以包含目录,这些目录又可以包含目录,如此循环。
那么,有没有更优雅的解决方案呢?
应该在构造函数中执行需要一定时间的操作还是先构造对象再稍后初始化呢?
例如,在构造表示目录结构的对象时,对象及其子文件夹的填充应在构造函数中完成。显然,一个目录可以包含目录,这些目录又可以包含目录,如此循环。
那么,有没有更优雅的解决方案呢?
总之:
至少,你的构造函数需要将对象配置到其不变条件为真的点。
你选择的不变条件可能会影响你的客户端。(对象承诺始终准备好访问吗?还是只在某些状态下才准备好?)一个处理所有设置的构造函数可能会让类的客户端更加简单。
长时间运行的构造函数本质上并不坏,但在某些情况下可能是不好的。
对于涉及用户交互的系统,任何类型的长时间运行的方法都可能导致响应性差,因此应该避免。
将计算延迟到构造函数之后可能是一种有效的优化;可能没有必要执行全部工作。这取决于应用程序,并且不应过早确定。
总的来说,这取决于具体情况。
通常情况下,您不希望构造函数进行任何计算。使用代码的其他人也不会期望它做更多的基本设置。
对于像您所说的目录树,"优雅"的解决方案可能不是在对象构建时构建完整的树形结构。相反,按需构建它。使用您的对象的人可能并不关心子目录中有什么,因此首先只让您的构造函数列出第一层级,然后如果有人想要进入特定的目录,则在请求时构建该部分树形结构。
时间不应该成为不在构造函数中放置代码的理由。您可以将代码本身放入私有函数中,并从构造函数中调用该函数,以保持构造函数的清晰性。
但是,如果您想要执行的任务不需要给对象定义条件,并且可以在首次使用时稍后完成这些任务,则将其放到后面并稍后完成这些任务是一个合理的选择。但不要让它依赖于您的类的用户:这些事情(按需初始化)必须对您的类的用户完全透明。否则,您的对象的重要不变量可能很容易被破坏。
这要看情况而定(典型的计算机科学答案)。如果您正在为一个长时间运行的程序构建对象,则在构造函数中执行大量工作没有问题。如果这是 GUI 的一部分,那么可能不合适。始终如此,最好的答案是首先尝试最简单的方法,进行性能分析并进行优化。
对于这种特定情况,您可以对子目录对象进行懒惰构造,只为顶级目录的名称创建条目。如果它们被访问,则加载该目录的内容。当用户浏览目录结构时再重复这个过程。
构造函数最重要的任务是给对象一个初始有效状态。在我看来,对构造函数最重要的期望是构造函数不应该有任何副作用。
ofstream
这样的构造函数,你仍然期望“没有副作用”吗? - João Portela在这里应该做的正确方式是简单地初始化您的目录对象。使用可以取消并保持UI响应状态的循环构建您的目录树(取消按钮应始终起作用)。
为了便于代码维护、测试和调试,我尽量避免在构造函数中放置任何逻辑。如果您喜欢从构造函数中执行逻辑,那么将逻辑放入init()等方法中并从构造函数中调用init()将会很有帮助。如果您计划开发单元测试,应该避免在构造函数中放置任何逻辑,因为这可能很难测试不同的情况。我认为之前的评论已经解决了这个问题,但是……如果您的应用程序是交互式的,则应避免调用单个导致明显性能损失的函数。如果您的应用程序是非交互式的(例如:夜间批处理作业),那么单个性能损失并不是很重要。
历史上,我编写构造函数时通常会使对象在构造函数方法完成后立即可用。涉及的代码多少取决于对象的要求。
例如,假设我需要在详细视图中显示以下公司类:
public class Company
{
public int Company_ID { get; set; }
public string CompanyName { get; set; }
public Address MailingAddress { get; set; }
public Phones CompanyPhones { get; set; }
public Contact ContactPerson { get; set; }
}
由于我想在详细视图中显示有关公司的所有信息,因此我的构造函数将包含填充每个属性所需的所有代码。 鉴于这是一个复杂类型,因此Company构造函数也将触发Address、Phones和Contact构造函数的执行。
现在,如果我正在填充目录列表视图,在那里我可能只需要CompanyName和主要电话号码,我可以在类上拥有第二个构造函数,该构造函数仅检索该信息并将其余信息留空,或者我可以创建一个单独的对象,仅包含该信息。 这实际上取决于从何处检索信息。
无论类上有多少个构造函数,我的个人目标是进行必要的处理,以准备对象承担任何可能加于它的任务。
RAII是C++资源管理的支柱,因此在构造函数中获取所需资源,在析构函数中释放它们。这时候你需要建立你的类不变式。如果需要时间,就花时间。你有越少的“如果X存在则执行Y”结构,设计该类就会更简单。后来,如果分析显示这是个问题,请考虑像延迟初始化(在首次需要资源时获取资源)这样的优化。