家长班:
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还是未定义?
答案 0 :(得分:1)
不允许这样的演员。这是一个基本问题:您必须通过复制进行转换,或者调整您的类定义,以便您拥有ListOf<PointT, T>
,即在点类型和点内类型上进行参数化。
但是,课程设计仍有缺陷:你不应该从Point3D
派生Point
,这违反了Liskov substitution principle(LSP) - 或更普遍:3D点是不 2D点。事实恰恰相反:2D点是3D点的特殊情况。
所以如果你希望在这里有继承,它应该采用另一种方式(即2D继承自3D),但这很可能也违反了LSP,并且非常尴尬(从那以后你的2D点会有一个总是固定的冗余变量)。简而言之,2D和3D点之间没有合适的继承关系,它们是不同的实体。
答案 1 :(得分:1)
reinterpret_cast保证唯一的事情就是从A *转换为B *然后再转回A *会产生原始指针。使用中间B *除了回退到A *以外的任何东西都是未定义的。
答案 2 :(得分:0)
这两种方式只是未定义的行为。
答案 3 :(得分:0)
鉴于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而不是相反。
但是,提供“通用点”容器可能更有意义;也就是说,它接受任何具体的类似Point的类型。
template<class Point>
struct GenericPointContainer {
private:
std::vector<Point> points;
};
这里最大的优势是GenericPointContainer的方法可以使用Point派生类中的各种特性,这些特性在Point本身中不存在,但仍然直接在Point上实例化。这是有效的,因为在实例化类模板时没有实例化所有方法,并且一个实际的例子是std :: reverse_iterator如何重载operator + =,它只适用于随机访问迭代器,但可以在非随机访问迭代器上实例化,例如std :: reverse_iterator&lt; std :: list&lt; int&gt; :: iterator&gt;。
然后,如果仍然需要,各种列表类可能会变成简单的typedef:
typedef GenericPointContainer<Point<int> > PointsList;
typedef GenericPointContainer<Point3DTopoList<int> > Points3DTopoList;
C ++ 0x真的会帮助你使用模板typedef(你可以在当前的C ++中使用重新绑定,但这会变得迟钝);正如你所看到的,我必须为typedef指定T,所以它不像其他那样通用。
答案 4 :(得分:0)