我有一个类层次结构,其中的每个类都有一个异常类,在并行层次结构中派生,因此...
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 ++。
答案 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()中使用。
如果不止于此,则需要更多的子类化。