STL+typedefs与OOP,最佳实践

STL + typedefs vs. OOP, best practices?

本文关键字:最佳 OOP STL+typedefs      更新时间:2023-10-16

我在学习使用OOP设置数据结构时"长大"了。但现在,随着我对C++、STL和Boost的了解越来越多,我发现我的许多数据结构需求都可以通过将STL类组合成更复杂的组合来满足,比如:

typedef std::map<std::string, std::map<std::string, int> > CSVData;

显然,当我需要混合数据类型时,这是有局限性的,但通常我发现自己会避免OOP,因为我可以支持这些STL复合材料,因为它们很简单。这是一个常见的进展吗?有没有最好的经验法则来定义你自己的类?这里面有常见的陷阱吗?

如果这是针对一些一次性代码:当然,使用typedefs,为什么不呢?

但代码很少是这样。可重用代码(或在未来某个时候可能被重用的代码)的问题是:有1000种不同的错误使用方式。不要人为地添加原因1001–1255。

代码应该易于使用,并且不易被滥用除其他外,这意味着:拥有一个明显的接口,并禁止所有不受支持、有意义或不必要的操作。提供typedef在这方面是失败的:您的CSV数据类型支持各种不相关的操作。

反对typedefs的原因还有很多:例如,当您需要更改数据结构的底层实现(需求的更改、性能问题…)时,这会使接口很容易中断。从本质上讲,如果不破坏typedef'd数据结构的所有用途,就无法做到这一点。

但是,只要接口保持不变,一个设计得当、封装良好的数据结构就可以很容易地更改其内部实现,而不会破坏消费者代码。

这是大型体系结构中最重要的概念之一:信息隐藏

此外,这是一个逻辑错误(尽管很小):您的typedef对逻辑is-a关系进行建模。但事实并非如此:CSV数据不是地图的映射。相反,它可以通过映射的映射来实现。这是一个根本的区别。

也就是说,C++确实教会了大型对象层次结构不需要面向对象:基本上,整个标准库(IO流库除外)不是面向对象的:而是面向算法的。但这并不意味着你不必编写自己的课程。

我认为随着年龄的增长,我们大多数人编写的OO代码越来越少。把一切都变成一个对象并构建拜占庭继承等级制度的冲动往往会在维护这些东西的经验中消退。

  • 不要重新发明轮子(如果它们符合您的目的,请使用现有的构造。)
  • 确保您的代码可读且可维护(读者会发现,即使明智地使用typedefs,也很难跟踪所有嵌套模板等。)

我倾向于避免在类之外使用纯typedef(除了在文件/函数中保存一些类型的典型别名)。

有两个(密切相关的)原因,两者都源于同一个事实:我可以控制我的类上可用的操作/方法:

  • 我可以给他们起合适的名字,与问题域有关,而不是通用的名字
  • 我可以控制这些方法的实现:维护不变量,添加调试信息/断言

当然,我会在类中使用typedef,并将大部分实现委托给那些已经编码的方法,但不要嘲笑它上面的薄层。这很有价值。

我认为正是在Effective C++(第三版)中,Scott Meyer强调C++支持几种不同的编程范式。对象定向就是其中之一。通用编程(STL风格)是另一种。还有其他人。

使用最适合你试图解决的问题的风格没有错。在一个更大的项目中,可能确实有一些部分你想用泛型来解决,还有一些部分你想要用面向对象来解决。你想知道自己处于哪种模式,但我认为两者兼而有之没有问题。

当你在学习新东西时,越来越多地使用它是很自然的。当你的泛型经验赶上OO经验时,你可能会注意到你的直觉中有一种更平衡的方法。

最好针对接口进行编程,而不是具体实现。

正如@adrian mccarthy所提到的,C++是一种多范式语言,有几种不同的方法可以针对接口进行编程。

一种方法是使用OOP。

另一种方法是编写模板。

与其直接使用typedef,不如将客户端代码模板化,以便与实现相同概念的任何容器一起工作。(Boost静态断言和Boost概念检查功能可能有助于准确锁定算法所依赖的概念。)

当然,模板化的决定意味着对代码组织的某些非琐碎的更改(将方法实现移动到头中)。这是否合适由你来决定。