【问题标题】:Extending enum in derived classes [duplicate]在派生类中扩展枚举
【发布时间】:2010-09-07 14:14:57
【问题描述】:

我有一个类层次结构,其中的每个类都有一个异常类,派生在一个并行层次结构中,因此...

class Base
{
};

class Derived : public Base
{
};

class BaseException : public std::exception
{
   enum {THIS_REASON, THAT_REASON};
};

class DerivedException : public BaseException
{
    // er...what?
};

我想在 DerivedException 类中扩展枚举类型以包含一个新值 THE_OTHER_REASON,以便 DerivedException 类可以保存这三个值中的任何一个。

首先,我应该这样做吗?这似乎是一种合理的做法?如果是这样,我该怎么做?如果没有,您会推荐哪些替代方案?

编辑:已建议可能重复 here,但建议的解决方案不同,因为该问题针对 C#,而针对 C++。

【问题讨论】:

  • @JayElston 我想知道以前没有人注意到:您的可能重复是给c#的。 C# 中的限制可能类似,但我不认为这是一个适当的重复。

标签: c++ inheritance


【解决方案1】:

从 OO 的角度来看,这是不合理的。既然你说DerivedException is-a BaseException,那么它的可能原因一定是BaseException子集,而不是超集。否则你最终会破坏Liskov Substitution Principle

此外,由于 C++ 枚举不是类,因此您不能扩展或继承它们。您可以在 DerivedException 中的单独枚举中定义其他原因,但最终您会遇到上述相同的问题:

class DerivedException : public BaseException
{
  enum {
    SOME_OTHER_REASON = THAT_REASON + 256, // allow extensions in the base enum
    AND_ANOTHER_REASON
  };
  ...
};

...
try {
  ...
} catch (BaseException& ex) {
  if (ex.getReason() == BaseException::THIS_REASON)
    ...
  else if (ex.getReason() == BaseException::THAT_REASON)
    ...
  else if (ex.getReason() == ??? what to test for here ???)
    ...
}

您可以做的是为每个不同的原因定义一个单独的异常子类。然后你可以多态地处理它们(如果需要)。这是标准 C++ 库以及其他类库的方法。因此,您遵守约定,这使您的代码更易于理解。

【讨论】:

  • +1 表示 Liskov 替换原则。如果扩展,那么您需要某种映射回基本代码集。
  • 但这与我的限制相同。当您使用超过 2 个派生枚举时,您无法确定值是否来自另一个枚举
  • @Miro,显然你没有注意到我绝对不建议以任何方式扩展枚举 :-)
  • 是的,它不是,我知道。也许在没有经常使用的“枚举”中更好地做任何工厂来处理和注册值,但这需要每次检查“枚举”时使用额外的 var 保存注册值,这使得简单的更高内存使用率
  • @Péter,您能否详细说明异常类的“这是标准 C++ 库的方法”,提供一些示例和/或详细说明/示例的参考/链接?谢谢。
【解决方案2】:

对我来说,为每个异常原因设置一个异常类似乎更合理。 在处理异常时,通常不知道是哪个类抛出了异常,而是因为什么原因抛出了异常。

如果您想保留您的设计:C++ 不允许您扩展现有枚举,但您可以创建一个新枚举,从前一个枚举的中断处开始:

class BaseException : public std::exception
{
   enum {THIS_REASON, THAT_REASON, END_OF_BASE_REASONS };
};

class DerivedException : public BaseException
{
   enum {OTHER_REASON = BaseException::END_OF_BASE_REASONS };
};

【讨论】:

    【解决方案3】:

    您可以这样做,因为您使用未命名的枚举。我猜你使用整数类型来存储异常的原因。

    class DerivedException : public BaseException
    {
        enum { YET_ANOTHER_REASON = THAT_REASON + 1, EVEN_MORE_REASON};
    };
    

    我不会将原因编码为代码。我更喜欢专门的异常类,所以我只能捕获我可以同时处理的异常类型。

    【讨论】:

      【解决方案4】:

      首先:不能导出枚举;你只是运气不好。

      其次,听起来您在考虑异常模型。与其让每个类都使用特定于该类的自定义派生异常,不如构建一系列具有您的类可以抛出的语义值的异常。例如,如果您因为必需的参数为空而抛出,您可能会抛出NullArgumentException。如果您因为数学溢出而抛出,您可能会抛出ArithmeticOverflowException

      在您的枚举模型中,您将语义值放在枚举上;我建议您将语义值放在异常的类型上。请记住,您可以捕获多种类型的异常。

      有关语义值异常的示例,请查看标准 C++ 库,或者查看更广泛的列表,查看 Java 或 C# 库。

      【讨论】:

        【解决方案5】:

        不,就目前而言,这不合理。为了使派生类型具有任何意义(从 Liskov 的替换原则的角度来看),基类中需要有多态行为。

        您可以将virtual int GetError() const 添加到基类并让派生类覆盖它,但是BaseException*BaseException& 的用户将不知道返回的错误代码是什么派生类的意思。

        我会将错误代码值与类分开。

        【讨论】:

          【解决方案6】:

          我想在 DerivedException 类中扩展枚举类型以包含一个新值 THE_OTHER_REASON,以便 DerivedException 类可以保存这三个值中的任何一个。

          只需分配新枚举的第一个值。这是可行的,因为您只是将枚举用作声明常量的一种方式。

          class DerivedException : public BaseException
          {
              enum {THE_OTHER_REASON = THAT_REASON + 1, THE_REALLY_OTHER_REASON, ETC};
          };
          

          问题在于,如果不定义派生类的“链”(即一个枚举在另一个枚举之后开始),您就不能真正期望派生类之间枚举的唯一性。但是,如果您只需要异常类树的单个分支中的唯一性,它就可以正常工作。

          【讨论】:

            【解决方案7】:

            异常的“原因”很可能是需要向用户显示或记录的东西,因此字符串似乎更合适,因此可以在what()中使用。
            如果不止于此,则需要更多的子类化。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-04-04
              • 2012-07-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多