在类树中使用动态强制转换是否可以接受
Is it acceptable to use dynamic casting for dynamic converstion within a class tree?
对于大学作业,我正在构建一个类结构,其中部分包括几个Pixel
类,每个类都使用特定的颜色空间(如8位灰度、24位RGB等)。
大部分工作由Image::Base&
完成,它将使用Pixel::Base&
,因此不知道任何Pixel
分配的每一侧都有什么特定类型的Pixel
。
因此,为了允许在未确定的子类型之间进行转换,我使用转换构造函数和operator=
,通过基类中的虚拟函数。我看到了两个选项,要么每个类都必须实现to_Grey8()
、to_RGB()
等等——以拥有许多单独的转换函数为代价,使转换构造函数和operator=
变小——要么我让接收对象自己进行转换(结果相反)。
出于某种原因,我最初选择了后一种选择。
问题是,转换为RGB(例如)希望复制其他对象的内部值,如果恰好也是RGB对象,但如果其他对象只是Base&
,则无法做到这一点,因此,我最终使用dynamic_cast
来检查每种需要特殊处理的不同类型的Pixel
——随着子类型数量的增长,可能会有很多。
我觉得这种方法可能"不好",所以:
这是dynamic_cast
的正确/有效/安全用法吗
如果没有,为什么?为了实现同样的目标,什么是合理的替代方案
仅关注operator=()
,以下是定义的重要部分:
class Base
{
public:
virtual Pixel::Base& operator=( Pixel::Base const& rhs ) = 0;
}
class RGB24 : public Base
{
private:
unsigned char r, g, b;
public:
virtual Pixel::RGB24& operator=( Pixel::Base const& rhs );
}
class Grey8: public Base
{
private:
unsigned char i;
public:
virtual Pixel::Grey8& operator=( Pixel::Base const& rhs );
}
实现如下所示:
Pixel::Grey8& Grey8::operator=( Pixel::Base const& rhs )
{
i = rhs.intensity();
return *this;
}
Pixel::RGB24& RGB24::operator=( Pixel::Base const& rhs )
{
try {
auto castrhs = dynamic_cast<Pixel::RGB24 const&>(rhs);
r = castrhs.r;
g = castrhs.g;
b = castrhs.b;
return *this;
} catch (std::bad_cast& e) {}
//TODO other casting blocks will go here as other Pixel classes are added
//no specific handler worked, so just effectively greyscale it
r = rhs.intensity();
g = rhs.intensity();
b = rhs.intensity();
return *this;
}
如果你不需要绝对的最佳效率,那么你可以考虑将转换分为两个步骤:将像素类型T1转换为具有最高颜色分辨率的"通用中间"类型(我称之为RGB48),然后从该类型转换为T2。这样,您只需要编写2*N个转换函数,而不需要编写N^2个转换函数。你最终会得到这样的东西:
Pixel::RGB24& RGB24::operator=( Pixel::Base const& rhs )
{
Pixel::RGB48 rgb48pixel = rhs.toRGB48();
r = rgb32pixel.r/256; // downsample
g = rgb32pixel.g/256; // downsample
b = rgb32pixel.b/256; // downsample
}
所有Pixel类型都必须定义toRGB48()——它在基类中被声明为抽象虚拟方法。
在回答您最初的问题(dynamic_cast是否可以/安全/等)时:有时使用它是合适的,但它通常被认为是一种"代码气味"。但如果你有真正的多重调度,也就是说,你需要一些依赖于两种类型的操作,而不能用一些中间通用表示法分解为两个步骤,那么它实际上是唯一的选择。在其他情况下也可以这样做。它当然是安全/有效的,只是有时它是次优设计的标志。
如果你确实使用动态调度,那么我建议你以以下方式使用它:
if (auto casthrs = dynamic_cast<Pixel::RGB24 const>(&rhs)) {
r = castrhs->r;
g = castrhs->g;
b = castrhs->b;
return *this;
}
(即dynamic_cast是一个指针,而不是引用——如果失败,它将返回0,而不是抛出异常。)这样可以避免异常处理的开销,而且我发现它也更容易阅读。
在我看来,您正在使用的工具(重写equals、使equals成为虚拟函数和动态转换)不太适合这个项目。如果您正在将一种类型的像素转换为另一种类型,那么您可能正在转换潜在的大型位图。在这种情况下,性能是关键。我会在每个位图而不是每个像素的基础上实现转换。如果选角可能很昂贵,那么就不应该按像素进行。
我建议暂时放弃我上面提到的语言的特点。我认为他们在这种情况下没有增加价值,事实上他们正在使解决方案成为问题。使用更原始的编程结构重新思考解决方案。
- 是否可以从int转换为enum类类型
- 是否可以将llvm::FunctionType转换为C/C++原始函数指针
- 将QOpenGLWidget子类转换为使用Metal而不是OpenGL的子类是否可行?
- 是否应避免从非常量迭代器转换为常量迭代器?
- 是否有内置方法可以强制转换为不同的基础类型,但保留常量限定符?
- 是否可以将带有字符串化运算符的宏转换为 constexpr?
- 将INT32BE宏转换为 constexpr 是否正确?
- 如果整数与指针大小相同,则重新解释将整数转换为指针双射是否具有双射作用?
- 如何将 UTF-8 文本从文件转换为某个可以迭代的容器,并检查每个符号是否为C++字母数字?
- 是否可以在运行时强制转换模板参数?
- 错误:在尝试检测 std::cout 是否<< t 时,功能强制转换为数组类型;有效
- 将 C 函数转换为 C++ 以检查数字是否有效
- 是否可以将无符号 int 的最大值转换为 int 并将结果转换为 -1?
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- vec[i][j] 是否转换为 *(vec + i + j)?
- 隐式转换是否应该在模板参数的上下文中工作?
- 是否可以创建一个用户定义的文本,将字符串文本转换为 own 类型的数组?
- 是否可以将已经初始化的变量转换为 void*?
- 在比较过程中C++布尔值是否转换为整数
- 如何正确地从字符数组中删除字符(是否转换为字符串)