【问题标题】:C++ reinterpret_cast for derived class派生类的 C++ reinterpret_cast
【发布时间】:2011-06-10 22:26:58
【问题描述】:

父类:

template <class T>
class Point
{
    protected

        T x;
        T y;

};

派生类:

template <class T>
class Point3DTopo: public Point <T>
{
    protected:

        T z;
        Face <T> *face;   //Points to any face
};

我想将 PointsList 类的一个对象转换为另一个对象 Points3DTopoList(反之亦然),其中:

template <class T>
class PointsList
{
  protected:
         std::vector <Point <T> *> points;  //Only illustration, not possible with   templaes
};


template <class T>
class Points3DTopoList
{
  protected:
         std::vector <Point3DTopo <T> *> points;  //Only illustration, not possible with   templaes
};

允许这样的转换吗?

Points3DTopoList <T> *pl = new Points3DTopoList <T> ();
...
PointsList <T> *pl = reinterpret_cast < PointsList <T> * > ( pl3D );

还有反向转换?

PointsTopoList <T> *pl = new PointsTopoList <T> ();
...
Points3DTopoList <T> *pl3D = reinterpret_cast < Points3DTopoList <T> * > ( pl );

每个 Point3Topo 的 Face 指针会被初始化为 NULL 还是未定义?

【问题讨论】:

  • 再来一次?让我重复一遍:这是一个糟糕的主意,不要这样做。您不需要reinterpret_cast,您只需要花时间将一个容器逐点转换为另一个容器。而已。这里没有技巧,你不需要它们,只是一个简单的循环。再次,停止动态分配一切!
  • 除了你应该使用dynamic_cast的所有东西

标签: c++ derived-class reinterpret-cast


【解决方案1】:

不允许这样的演员表。这是一个基本问题:您要么必须通过复制进行转换,要么调整您的类定义,以便您拥有 ListOf&lt;PointT, T&gt;,即在点类型和点内的类型上都进行参数化。

但是,无论如何,类设计存在缺陷:您不应该从 Point 派生 Point3D,这违反了 Liskov substitution principle (LSP) - 或者更普遍地说:3D 点不是一个二维点。恰恰相反,事实上:2D 点是 3D 点的特例。

所以如果你想在这里继承,它应该采用另一种方式(即2D继承自3D)但这很可能违反LSP,并且非常尴尬(从那时起,您的 2D 点将有一个始终固定的冗余变量)。简单来说,2D点和3D点之间没有合适的继承关系,它们是不同的实体。

【讨论】:

  • 演员表是允许的,但对你可以对结果做什么有严格的限制。
  • 从 Point2D 派生 Point3D 效果很好,没问题。它可能不适合基于概念的数学观点,但这也没有问题。但是,当考虑(指向)此类对象的集合时,您会遇到问题,例如Java 的反应是引发一个异常:认为是一个 Point2D 的集合,一个 Point3D 的集合可以插入一个纯 Point2D。在 C++ 中,这可以通过常量和接口来避免。但它涉及到,考虑到椭圆/圆“辩论”的长度,我不应该提到它,抱歉。 ;-)
【解决方案2】:

reinterpret_cast 保证的唯一事情是从 A* 转换为 B* 然后再转换回 A* 会产生原始指针。将中间 B* 用于除强制转换为 A* 之外的任何事情都是未定义的。

【讨论】:

  • reinterpret_cast 还保证从 POD 的第一个成员转换为 POD 的类型,反之亦然。那是在课程部分的开始。如果 johannes 醒着,他可能还会指出 c++98 中的缺陷,其中 reinterpret_cast to/from void* 正式无效,已在 c++0x 中修复(但无论如何,AFAIK 没有编译器不允许 reinterpret_cast to/from void* )
  • 很好,@Alf。我已经稍微限定了我的答案。
【解决方案3】:

这只是两种方式的未定义行为。

【讨论】:

  • 允许在不相关的类型之间使用 reinterpret_cast。你对结果所做的事情可能会给你带来麻烦。
【解决方案4】:

鉴于 Point3DTopo 是从 Point 派生的,提供从 Points3DTopoList 到 PointsList 的转换可能是明智的。自动提供转换的继承是可能的,但我怀疑您的公共接口要求(从问题中省略)使它比资产更麻烦。

提供转化路径的示例:

template<class T>
struct PointsList {
  // Points3DTopoList needs a way to construct a PointsList
  template<class Iter>
  PointsList(Iter begin, Iter end)
  : points(begin, end)
  {}

private:
  std::vector<Point<T> > points;
};

template<class T>
struct Points3DTopoList {

  operator PointsList<T>() const {
    return PointsList<T>(points.begin(), points.end());
  }

  PointsList<T> to_points_list() const {
    return PointsList<T>(points.begin(), points.end());
  }

private:
  std::vector<Point3DTopo<T> > points;
};

这提供了两种转化路径——通常您会选择一种而不提供另一种。转换运算符是隐式转换(在 C++0x 中,您可以将其标记为显式),而命名方法在技术术语中不是“转换”(因此从不适用于任何隐式或显式转换),而是显式调用并以这种方式使用。

您还可以使用 PointsList 中的显式构造函数提供显式转换,该构造函数接受 Points3DTopoList,这在当前的 C++ 中有效,但代价是反转了通常的依赖关系:也就是说,PointsList 会知道并关心关于 Points3DTopoList 而不是相反。

但是,提供“通用点”容器可能更有意义;也就是说,它接受任何具体的点状类型。

template<class Point>
struct GenericPointContainer {
private:
  std::vector<Point> points;
};

这里最大的优势是 GenericPointContainer 的方法可以使用来自 Point 派生类的各种特性,这些特性在 Point 本身中不存在,但仍然可以直接在 Point 上实例化。这是因为在实例化类模板时不会实例化所有方法,一个实际的例子是 std::reverse_iterator 如何重载 operator+=,它仅适用于随机访问迭代器,但可以在非随机访问迭代器上实例化,比如 std::reverse_iterator<:list>::iterator>.

然后各种列表类可能会变成简单的 typedef,如果它们仍然需要的话:

typedef GenericPointContainer<Point<int> > PointsList;
typedef GenericPointContainer<Point3DTopoList<int> > Points3DTopoList;

C++0x 在模板 typedef 方面确实可以帮助您(您可以在当前 C++ 中使用重新绑定,但这会变得迟钝);如您所见,我必须为 typedef 指定 T,所以它不像其他方式那样通用。

【讨论】:

    【解决方案5】:

    我已经回答了:C++ reinterpret cast?

    而且它不会很慢,因为只会复制指针。

    您自己的演员阵容。

    【讨论】:

    • 您可以创建包装器类来表示来自 Points3DTopoList 的 PointsTopoList 和另一个包装器来表示来自 PointsTopoList 的 Points3DTopoList。在包装器中,您应该只复制指向数据的指针,而不是复制所有数据。
    • 您可以在 Point 类中包含所有数据变量(包括 x、y、z 等)。您将使用此类在 Points3DTopoList (std::vector *> points;) 中创建向量,在 PointsTopoList 中创建相同的向量。在这种情况下,您不需要任何演员表。
    • 您可以使用任何其他好的想法,但不能使用未定义行为的想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    相关资源
    最近更新 更多