在C中省略“析构函数”是否会使YAGNI过分?


9

我正在使用类似于OO的技术来开发C中的嵌入式媒体应用程序。我的“类”是.h / .c模块,它们使用数据结构和函数指针结构来模拟封装,多态和依赖项注入。

现在,人们期望一个myModule_create(void)功能会与之myModule_destroy(pointer)对应。但是,在嵌入的项目中,切勿释放实际实例化的资源。

我的意思是,如果我有4个UART串行端口,并使用所需的引脚和设置创建4个UART实例,则绝对没有理由要在运行时的某个时候销毁UART#2。

因此,遵循YAGNI(您将不需要它)原则,我应该省略析构函数吗?这对我来说似乎很奇怪,但我想不出对他们有什么用。设备下电后释放资源。


4
如果您没有提供处置对象的方法,那么您将传递一条明确的消息,即一旦创建对象,它们的生命周期就“无限”。如果这对您的应用程序有意义,我会说:做到这一点。
glampert 2014年

4
如果要在将类型与特定用例相结合方面走得那么远,那为什么还要有一个myModule_create(void)功能呢?您可以将要使用的特定实例硬编码到公开的接口中。
2014年

@Doval我考虑过了。我是一名实习生,使用的是我主管的部分代码,所以我想尝试“正确地做”,尝试用C进行OO风格的体验,并与公司的标准保持一致。
2014年

2
@glampert钉它;我要补充一点,您应该在create函数的文档中将预期的无限期清理干净。
Blrfl 2014年

Answers:


11

如果您没有提供处理对象的方法,那么您将传递一条明确的消息,即一旦创建对象,它们的生命周期就“无限”。如果这对您的应用程序有意义,我会说:做到这一点。

格伦佩特是对的。这里不需要析构函数。他们只会给用户造成代码膨胀和陷阱(在调用其析构函数后使用对象是未定义的行为)。

但是,您应该确保确实不需要处理这些对象。例如,您是否需要一个当前未使用的UART对象?


3

我发现检测内存泄漏的最简单方法是能够干净退出应用程序。许多编译器/环境都提供一种方法来检查在应用程序退出时仍分配的内存。如果没有提供,通常有一种方法可以在退出前添加一些代码,以便找出答案。

因此,即使在嵌入式系统中,我也肯定会提供构造函数,析构函数和关闭逻辑,而从理论上讲,它们绝不能为了简单地进行内存泄漏检测而退出。实际上,如果应用程序代码永不退出,则内存泄漏检测甚至更为重要。


请注意,如果您唯一的分配时间是在启动期间,则此方法不适用,这是我在内存限制设备中认真考虑的一种模式。
CodesInChaos

@Codes:没有理由限制自己。尽管您可能会想到在启动时预先分配内存的出色设计,但是当您跟随的人们不了解这种宏伟计划或看不到它的重要性时,他们会在服务器上分配内存。飞,然后去设计。只要正确地做,然后分配/取消分配,并验证您实现的内容是否确实有效。如果您确实有一个内存受限的设备,那么通常要做的是覆盖新的operator / malloc和prereserve分配块。
Dunk 2014年

3

在我的开发中,它广泛使用不透明的数据类型来培养类似于OO的方法,我也为这个问题而苦恼。最初,我绝对是从YAGNI角度以及MISRA“死代码”角度消除析构函数的阵营。(我有足够的资源余地,这不是一个考虑因素。)

但是,缺少析构函数会使测试更加困难,就像在自动化的单元/集成测试中一样。按照惯例,每个测试都应支持设置/拆卸,以便可以创建,操作然后销毁对象。销毁它们是为了确保进行后续测试的干净无污染的起点。为此,该类需要一个析构函数。

因此,以我的经验,YAGNI中的“不是”变成“是”,而我最终为每个类都创建了析构函数,无论我是否需要它。即使我跳过测试,对于跟随我的较差的Slob,至少也存在一个正确设计的析构函数,该析构函数将具有一个。(而且,由于可以在可能被破坏的环境中使用该代码,因此使代码更易于重用,从而使其价值更低)。

虽然该地址用于YAGNI,但不用于处理无效代码。为此,我发现像#define BUILD_FOR_TESTING这样的条件编译宏允许从最终生产版本中消除析构函数。

这样做:您具有用于测试/将来重用的析构函数,并且满足YAGNI的设计目标和“无无效代码”规则。


#ifdefing您的测试/产品代码时要小心。正如您所描述的,将其应用于整个函数是相当安全的,因为如果实际上需要该函数,则编译将失败。但是,在函数内部使用#ifdef内联的风险要大得多,因为您现在要测试与prod中运行的代码路径不同的代码路径。
凯文

0

你可以有一个无操作的析构函数,例如

  void noop_destructor(void*) {};

然后设置Uart可能使用的析构函数

  #define Uart_destructor noop_destructor

(如果需要,请添加合适的演员表)

不要忘记记录。也许你甚至想要

 #define Uart_destructor abort

或者,在常见代码中调用析构函数的特殊情况是指析构函数指针函数NULL避免调用它的情况。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.