类型字段是纯粹的邪恶吗?

时间:2010-09-27 21:56:54

标签: c++ code-organization

正如第12.2.5节The c++ Programming Language 3rd Edition中所讨论的那样,类型字段倾向于创建比使用虚函数和多态的等效代码更不通用,容易出错,更不直观和更少可维护的代码。 / p>

作为一个简短的例子,下面是如何使用类型字段:

void print(const Shape &s)
{
  switch(s.type)
  {
  case Shape::TRIANGE:
    cout << "Triangle" << endl;
  case Shape::SQUARE:
    cout << "Square" << endl;
  default:
    cout << "None" << endl;
  }
}

显然,这是一个噩梦,因为为此添加了一种新的形状,十几种类似的功能会容易出错并且很费力。

尽管有这些缺点以及TC ++ PL中描述的那些缺点,但是有没有这样的实例(使用类型字段)比使用虚函数的语言特性更好的解决方案? 或者这种做法是否应该被列为纯粹的邪恶?

现实的例子比人为的例子更受欢迎,但我仍然对人为的例子感兴趣。另外,您是否曾在生产代码中看到过这种情况(即使虚拟函数更容易)?

9 个答案:

答案 0 :(得分:7)

当你“知道”你有一个非常具体的,小的,不变的类型集时,就可以更容易地对它们进行硬编码。当然,常量不是,变量也不是,所以在某些时候你可能不得不重写整个事情。

这或多或少是用于Alexandrescu's articles中几个的受歧视联盟的技术。

例如,如果我正在实现一个JSON库,我知道每个Value只能是一个Object,Array,String,Integer,Boolean或Null - 规范不允许任何其他值。

答案 1 :(得分:3)

类型枚举可以通过memcpy序列化,v表不能。类似的功能是类型枚举值的损坏很容易处理,v表指针的损坏意味着即时的坏处。甚至没有可移植的方法来测试v表指针的有效性,调用dynamic_casttypeinfo对无效对象进行RTTI检查是未定义的行为。

例如,我选择使用具有由鉴别器控制的静态分派的类型层次结构而不是动态分派的一个实例是通过Windows消息队列将指针传递给结构时。这为我提供了一些保护,可以防止可能已经从我正在使用的范围中分配广播消息的其他软件(它应该保留给应用程序本地消息,如果您认为该规则实际受到尊重,则不通过GO。)

答案 2 :(得分:2)

以下指南来自Robert C. Martin的Clean Code。 “我对switch语句的一般规则是,如果它们只出现一次,用于创建多态对象,并且隐藏在继承关系后面,以便系统的其余部分看不到它们,则可以容忍它们。”

基本原理是:如果将类型字段公开给其余代码,您将获得上述switch语句的多个实例。这显然违反了DRY。添加类型时,所有这些开关都需要更改(或者更糟糕的是,它们会在不破坏构建的情况下变得不一致)。

答案 3 :(得分:1)

我的看法是:这取决于。

参数化Factory Method design pattern依赖于这种技术。

class Creator {
    public:
        virtual Product* Create(ProductId);
};

Product* Creator::Create (ProductId id) {
        if (id == MINE)  return new MyProduct;
        if (id == YOURS) return new YourProduct;
        // repeat for remaining products...

        return 0;
}

所以,这很糟糕。我不这么认为,因为我们现阶段没有其他选择。这是一个绝对必要的地方,因为它涉及创建一个对象。对象的类型尚不清楚。

然而,OP中的示例是一个肯定需要重构的示例。这里我们已经处理了一个现有的对象/类型(作为参数传递给函数)。

正如Herb Sutter提到的那样 -

  

“关闭:避免打开   要自定义的对象的类型   行为。使用模板和虚拟   函数让类型(不是他们的   调用代码)决定他们的行为。“

答案 4 :(得分:0)

是否存在与虚函数和多态相关的成本?就像每个类维护一个vtable一样,每个类对象大小增加4个字节,运行时间缓慢(我从未测量过),以便适当地解析虚函数。因此,对于简单情况,使用type字段似乎是可以接受的。

答案 5 :(得分:0)

我认为如果类型与隐含类完全对应,那么类型是错误的。它变得复杂的地方是类型不匹配或不那么切割和干燥的地方。

举个例子,如果类型是红色,绿色,蓝色。这些是形状类型。您甚至可以将颜色类作为mixin;但它可能太多了。

答案 6 :(得分:0)

我正在考虑使用类型字段来解决矢量切片的问题。也就是说,我想要一个分层对象的向量。例如,我希望我的矢量是一个形状矢量,但我想存储圆形,矩形,三角形等。

由于切片,你不能以最明显的简单方式做到这一点。所以通常的解决方案是使用指针或智能指针的向量。但我认为有些情况下使用类型字段将是一个更简单的解决方案,(避免使用新的/删除或替代生命周期技术)。

答案 7 :(得分:0)

我能想到的最好的例子(以及我之前遇到过的那个)就是当你的类型集合被修复并且你想要做的函数集(取决于那些类型)是流动的时候。这样,当您添加新函数时,您可以修改单个位置(添加单个开关),而不是添加新的基本虚拟函数,其中实际实现分散在类型层次结构中的所有类中。

答案 8 :(得分:-1)

我不知道任何现实的例子。被摧毁的将取决于有一些很好的理由不能使用虚拟方法。