【问题标题】:C++ Matrix class hierarchyC++ 矩阵类层次结构
【发布时间】:2010-05-03 15:53:35
【问题描述】:

矩阵软件库是否应该有一个根类(例如,MatrixBase),其中更多专业(或更受约束的)矩阵类(例如,SparseMatrixUpperTriangluarMatrix 等? ) 导出?如果是这样,派生类是否应该公开/保护/私有派生?如果不是,它们是否应该由一个封装通用功能的实现类组成,并且在其他方​​面不相关?还有什么?

我正在与一位软件开发同事(我不是本人)讨论这个问题,他提到从更通用的类派生更受限制的类是一个常见的编程设计错误(例如,他使用了示例从Ellipse 类派生Circle 类(类似于矩阵设计问题)不是一个好主意)即使SparseMatrix“IS A”MatrixBase 确实如此。基类和派生类提供的接口对于基本操作应该是相同的;对于专门的操作,派生类将具有可能无法为任意MatrixBase 对象实现的附加功能。例如,我们可以只计算PositiveDefiniteMatrix 类对象的cholesky 分解;但是,对于基类和派生类,标量的乘法应该以相同的方式工作。此外,即使底层数据存储实现不同,operator()(int,int) 对于任何类型的矩阵类都应该按预期工作。

我已经开始研究一些开源矩阵库,看起来这是一个混合包(或者我正在查看一个混合包)。我正计划帮助重构一个数学库,这是一个争论的焦点,我想发表意见(除非真的有一个客观的正确回答这个问题) 关于什么设计理念是最好的,以及任何合理方法的优缺点是什么。

【问题讨论】:

    标签: c++ oop


    【解决方案1】:

    Ellipse 的 Circle 子类(或 Rectangle 的 Square 子类)的问题发生在您可以修改每个 Ellipse 接口的一个维度时,使圆不再是圆(并且正方形不再是正方形) .

    如果您只允许不可修改的矩阵,那么您是安全的,您可以以自然的方式构建您的类型层次结构。

    【讨论】:

    • 是的,我更喜欢不可变数据,尽管我知道这在 C++ 中并不常见。
    • +1 用于以实际术语讨论 Cicle/Ellipse 示例的漏洞问题,而不涉及关于两者之间关系的概念理论。根据维护不变量等实际需求,很容易理解问题。
    【解决方案2】:

    呵呵呵呵。起初我读到你的朋友说圆应该是椭圆,并写了一篇长篇大论来说明为什么他们充满了它。

    你应该听你的朋友的话,除了我希望他们没有说 SparseMatrix“是一个”MatrixBase。该术语在现实世界与建模世界中意味着不同的事物。在建模世界中,“is-a”意味着遵循 Liskov 替换原则(查一下!)。或者,这意味着 SparseMatrix 必须遵循 MatrixBase 的约定,即成员函数不得要求任何额外的前置条件,并且必须满足不少于后置条件。

    我不确切知道这如何适用于矩阵问题,但如果您查看我在上一段中使用的术语(LSP 和按合同设计),那么您应该在学习答案的道路上做得很好问题。

    可能适用于您的情况的一种方法是在您的层次结构中获取各种共性并使其成为抽象接口。然后从那些正确响应它们的类中继承这些接口。这将允许您编写应该允许常用的函数,但仍然在有太多变化时保持分离。

    【讨论】:

    【解决方案3】:

    这是一个很好的问题,但我还不确定你想用什么指标来评估它。

    值得一提的是,我目前使用最多的一个 Matrix 库是 Armadillo 确实有一个使用 奇怪地重复出现的 remplate 模式的通用 Base 对象。我相信Eigen(另一个最近且大量模板化的矩阵库)也是如此。

    【讨论】:

    • 这些指标可以是一般软件的任何常用指标(正确性、效率、可维护性等),并且没有额外的信息可以假设您可以自己为矩阵排序图书馆。
    • 那么 CRTP 究竟是如何提供帮助的呢?它不是有效的向下转换模式(通常称为 SO 上的代码异味)吗?例如,template class MatrixBase { void interface() { static_cast(this)->implementation(); } }; UpperTriangularMatrix : MatrixBase { void implementation(); };因此,如果接口和实现指的是矩阵乘法,那么接口调用委托给派生类中正确的实现调用的想法是什么?用法,客户端只调用接口?
    • 有没有像有帖子一样格式化cmets?反引号不适用于上述评论。
    • 你必须和康拉德讨论犰狳的设计。我只是用它。至于格式,我也很遗憾,不知道有什么解决办法。
    • 经过进一步调查,CRTP 似乎只是在实现中用于获取编译时间而不是运行时间多态性,因此我认为这实际上只与性能有关。
    【解决方案4】:

    像这样的基于继承的设计要注意的主要问题是切片。

    假设 MatrixBase 定义了一个非虚拟赋值运算符。它复制所有矩阵子类共有的所有数据成员。您的 SparseMatrix 类定义了额外的数据成员。现在我们写这个会发生什么?

    SparseMatrix sm(...);
    MatrixBase& bm = sm;
    bm = some_dense_matrix;
    

    这段代码没有什么意义(试图通过基类中定义的运算符直接将 DenseMatrix 分配给 SparseMatrix)并且容易出现各种令人讨厌的切片行为,但是这种代码的一个脆弱方面并且有一个非常如果您提供可通过 MatrixBase*/MatrixBase& 访问的赋值运算符,这种情况很有可能发生。即使我们有这个:

    SparseMatrix sm(...);
    MatrixBase& bm = sm;
    bm = some_other_sparase_matrix;
    

    ...由于赋值运算符是非虚拟的,我们仍然存在切片问题。如果没有公共基类的继承,我们可以提供赋值运算符来有意义地将密集矩阵复制到稀疏矩阵,但是尝试通过公共基类来做到这一点很容易出现各种问题。

    一般应该避免基类的赋值运算符!想象一下这样一种情况,Dog 和 Cat 继承自 Mammal 并且 Mammal 提供了一个赋值运算符,不管是不是虚拟的。这意味着我们可以将 Dogs 分配给 Cats,这没有任何意义,即使操作员是虚拟的,也很难提供任何有意义的行为来将哺乳动物分配给其他哺乳动物。

    假设我们试图通过在 Dog 中实现赋值运算符来改善这种情况,以便它只能分配给其他狗。现在,当我们从 Dog 继承来创建吉娃娃和杜宾犬时会发生什么?我们不应该将吉娃娃分配给杜宾犬,因此原始情况会递归重复,直到您确定您已经到达继承层次结构的叶节点(很遗憾 C++ 没有 final 关键字来防止任何进一步继承)。

    对于常见的 Circle 继承 Ellipse 示例,同样的问题很明显。圆可能需要宽度和高度才能匹配:这是类想要维护的不变量,但任何人都可以简单地获取指向 Circle 对象的基指针 (Ellipse*) 并违反该规则。

    如果有疑问,请避免继承,因为这是 C++ 和任何支持面向对象编程的语言中最常被滥用的特性之一。您可以尝试通过提供运行时机制来解决该问题,以确定分配给另一个子类的子类的类型并且只允许匹配类型,但是现在您正在做大量额外的工作并产生运行时开销。对于继承层次结构,最好避免使用赋值运算符,并依赖克隆等方法来生成副本(原型模式)。

    因此,如果您选择从矩阵类中创建继承层次结构,您应该仔细考虑继承的(最有可能的短期)优势是否超过长期劣势。您还应该确保避免可能发生切片的所有潜在情况,这对于矩阵库来说可能很难做到而不影响其可用性和效率。

    【讨论】:

      【解决方案5】:

      拥有一个 Matrix 基类,该基类具有允许您构建特定矩阵的方法是否有用?例如类似(一个非常简单的例子):

      MatrixClass m;
      m.buildRotationMatrix(/*params*/)
      // Now m is a rotation matrix
      

      这在 OpenSceneGraph 框架中使用,非常适合我们的目的。但是,构建方法只是简单的旋转或反转等。但我觉得它可以让你避免派生许多矩阵子类的问题。

      【讨论】:

        【解决方案6】:

        如果有足够多的通用方法和成员来保证基类,那么应该有一个和继承。我不会将基类用作所有矩阵的通用类型,而是用作通用方法和成员的容器(使构造函数受保护)。

        与 Java 不同,并非每个类或结构都需要基类。记住简单;复杂性使项目变得更长、更难管理和更难得到正确。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-19
          • 1970-01-01
          • 2017-04-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多