通过继承实现类似"二维"扩展的设计类

Design classes for "two-dimension"-like expansion by inheritance

本文关键字:二维 扩展 设计类 继承 实现      更新时间:2023-10-16

我想创建两个基本类(例如figure和move),每个类都有一些孩子(figure的立方体,球体等;移动时可以移动、旋转、缩放等)。最初的数字和移动是未知的-它必须是可扩展的。每个移动应该知道如何移动每个图形,所以有N个图形和M个移动意味着它们有N*M个函数。(添加一个移动需要为每个已经存在的图形创建N个函数,添加图形需要为每个已经存在的移动创建M个函数)。

问题是如何声明这些函数?例如,我将有一个包含图形列表(又名vector)的类Set,并且我需要要求这个类通过第I次移动所有的图形。set可能有

方法
set::move_all (const move& ) 

…接下来呢?最简单的方法是创建虚拟方法

class figure { 
    ...
    virtual void move_this (const move& ) 
    ...
}

调用虚方法move_figure

class figure_i: public figure {
    ...    
    virtual void move_this (const move& M)
       {M.move_figure(*this);
       }  
    ... 
}
class move {
    ...
    template <class T> virtual void move_figure (T&) const
    ...
}

并对每第i次移动进行专门化

template <> void shift::move_figure <cube> (cube& C)
    {
    }

等等,但是虚拟模板是非法的。

你把事情弄得太复杂了。您有一个对象集合(您称之为figures)和一个动作集合(您称之为moves)。从面向对象的角度来看,显而易见的选择是在figures类中使用moves方法。

更新

根据下面的评论,您应该使用线性代数库,如boost::ublas。还有其他一些您可能想要研究的,例如Eigen(或多或少如下所示)。

基本思想是抽象图形和移动,以概括两者,这样您就不必为每种可能的组合重写代码。因此,您将创建一个类似于

的基类:
template <typename T> class figure
{
    std::vector<Eigen::Vector3d<T> > point_list;
    ...
    void applyTransform(const Eigen::Affine3d<T>& src)
    {
       for (auto pt=point_list.begin(); pt != point_list.end(); pt++)
           (*pt) = src * (*pt);
    }
}

在这种情况下,您根据要渲染的形状定义点列表。您可以调整派生类中点的含义,以定义您感兴趣的特定几何图形。Eigen::Affine3D类用于定义您想要应用的转换。Eigen已经定义了旋转和其他仿射变换,所以你应该能够重用它们。

你也可以查看一些专门的OpenGL或DirectX几何类,它们为你做所有这些

嗯,我目前的解决方案是使用typeid/typeinfo来识别图形/移动对,并调用相应的函数(非成员),通过从map类型的全局对象中移动图形

typedef pair<string, string> fm_pair_t;
typedef figure (*fm_act_f) (const figure& F, const move& M);
map<fm_pair_r, fm_act_f> global_fm_map;
class move {
   ...
   figure move_figure (const figure& F) const
     {map<fm_pair_r, fm_act_f>::const_iterator i = 
          global_fm_map.find (fm_pair_t(typeid(F).name(), typeid(*this).name()));
      if (i == global_fm_map.end()) return F;
      return i->second (F, *this);
     }
   ...
};
某处

figure cube_shift (const figure& _F, const move& _M)
   {const cube& F = *dynamic_cast <const cube*> (&F);
    const shift& M = *dynamic_cast <const shift*> (&M);
    // act here with F and M like normal instances of cube and shift!
   };

当然还有

global_fm_map(typeid(cube).name(), typeid(shift).name()) = cube_shift;

所以与使用模板的情况不同,一切都是独立的,并且可以很好地扩展。