在派生类中扩展枚举

时间:2010-09-07 14:14:57

标签: c++ inheritance

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

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 ++。

8 个答案:

答案 0 :(得分:11)

从OO的角度来看,这是不合理的。由于您说DerivedException 是-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 :(得分:9)

对我来说,为每个异常原因设置一个异常类似乎更合理。 处理异常时,知道哪个类抛出异常通常没有意义,但抛出异常的原因是什么。

如果你想保留你的设计: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 };
};

答案 2 :(得分:2)

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

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

我不会将原因编码为代码。我更喜欢专门的异常类,所以我只能抓住我可以在某一时刻处理的异常类型。

答案 3 :(得分:1)

首先:枚举无法导出;你在那里运气不好。

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

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

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

答案 4 :(得分:1)

不,它现在不合理。对于派生类型具有任何意义(从Liskov的替换原则的角度来看),基类中需要存在多态行为。

您可以向基类添加virtual int GetError() const并让派生类覆盖它,但是BaseException*BaseException&的用户不会有任何关于什么的线索派生类返回的错误代码意味着。

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

答案 5 :(得分:1)

  

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

只需指定新枚举的第一个值即可。这是有效的,因为你只是使用枚举作为声明常量的方法。

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

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

答案 6 :(得分:1)

没有任何原生方式可以做到这一点,但可以通过定义轻松完成: 这是一个小例子:

#define _Enum1_values a, b, c, d

enum Enum1 {
    _Enum1_values
};

// there aren't strongly typed enums
class A {
public:
    enum Enum2 {
        _Enum1_values, e, f
    };
};

// there aren't strongly typed enums
class B {
public:
    enum Enum3 {
        _Enum1_values, g, h
    };
};


#include <iostream>

int main() {
    std::cout << "Enum1::d: " << d << '\n';
    std::cout << "Enum2::d: " << A::d << '\n';
    std::cout << "Enum2::e: " << A::e << '\n';
    std::cout << "WARNING!!!:  Enum2::e == Enum3::g: " << (A::e == B::g) << '\n';
}

答案 7 :(得分:0)

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