代码之家  ›  专栏  ›  技术社区  ›  JoshD

类型字段是纯邪恶的吗?

  •  4
  • JoshD  · 技术社区  · 14 年前

    如中所述 The c++ Programming Language 3rd Edition 在第12.2.5节中,与使用虚拟函数和多态性的等效代码相比,类型字段创建的代码的通用性、易出错性、直观性和可维护性较差。

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

    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 回复  |  直到 14 年前
        1
  •  7
  •   Roger Pate    14 年前

    当你“知道”你有一个非常具体的、小的、恒定的类型集时,像这样对它们进行硬编码会更容易。当然,常数不是,变量不是,所以在某个时刻,你可能需要重写整个过程。

    这或多或少是在一些国家中用于歧视工会的技术。 Alexandrescu's articles .

    例如,如果我正在实现 JSON 库,我知道每个值只能是一个对象、数组、字符串、整数、布尔值或空值。规范不允许任何其他值。

        2
  •  3
  •   Ben Voigt    14 年前

    类型枚举可以通过memcpy序列化,而v-table不能。类似的特点是类型枚举值的损坏很容易处理,v-table指针的损坏意味着即时的错误。没有可移植的方法来测试v-table指针的有效性,调用 dynamic_cast typeinfo 对无效对象执行RTTI检查是未定义的行为。

    例如,我选择使用由鉴别器控制的静态调度而非动态调度的类型层次结构的一个实例是通过Windows消息队列传递指向结构的指针。这给了我一些对其他软件的保护,这些软件可能已经分配了我正在使用的范围内的广播消息(它应该是为应用程序本地消息保留的,如果您认为该规则实际上得到了遵守,请不要通过Go)。

        3
  •  2
  •   Alex Emelianov    14 年前

    下面的指导原则来自罗伯特C.马丁的清洁代码。 “我对switch语句的一般规则是,如果它们只出现一次,用于创建多态对象,并且隐藏在继承关系后面,这样系统的其他部分就看不到它们,那么它们就可以被容忍。”

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

        4
  •  1
  •   Chubsdad    14 年前

    我的看法是:视情况而定。

    参数化的 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中的示例确实需要重构。这里我们已经在处理一个现有的对象/类型(作为参数传递给函数)。

    AS Herb Sutter 提及-

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

        5
  •  0
  •   Arun    14 年前

    是否存在与虚拟功能和多态性相关的成本?像维护每个类的vtable一样,每个类对象的大小增加4个byptes,运行时的慢度(尽管我从未测量过)可以适当地解析虚拟函数。因此,对于简单情况,使用 type 字段显示为可接受。

        6
  •  0
  •   pm100    14 年前

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

    以你的例子来说,如果类型是红、绿、蓝。这些是形状的类型。你甚至可以有一个颜色类作为混音,但它可能太多了。

        7
  •  0
  •   Bill Forster    14 年前

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

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

        8
  •  0
  •   Chris Dodd    14 年前

    我能想到的最好的例子(和我以前遇到的例子)是当你的类型集是固定的,你想做的功能集(取决于那些类型)是流动的。这样,当您添加一个新的函数时,您可以修改一个位置(添加一个开关),而不是添加一个实际实现分散在类型层次结构中的所有类中的新的基本虚拟函数。

        9
  •  -1
  •   Steven Sudit    14 年前

    我不知道任何现实的例子。人为设计的方法取决于是否有充分的理由不能使用虚拟方法。