[英]Handling multiple data representation for modifying data in vectors in C++
我有描述笛卡尔坐标系中二维点的简单数据结构,如下所示。
struct CartPoint
{
double x;
double y;
}
和第二个结构,代表极坐标系中的二维点
struct PolarPoint
{
double r;
double alpha;
}
还有两个函数允许我从一种表示转换为第二种表示:
void translate(const CartPoint& from, PolarPoint& to) { ... };
void translate(const PolarPoint& from, CartPoint& to) { ... };
我想创建 object (让我称之为PointContainer ),它允许我将笛卡尔二维点存储在一个向量中,但可以访问笛卡尔或极坐标表示(基于编译时决定)。 我在想一个 class 公开两种类型的非常量迭代器,一种用于每种表示。 但是,我在任何地方都找不到这样的解决方案,我不确定这是否是个好主意。 我想这样使用它:
void fillVectorWithCartPts(std::vector<CartPoint>& points)
{
// fills points-vector with 2d cartesian points
...
};
int main()
{
std::vector<CartPoint> pts{};
fillVectorWithCartPts(pts);
PointContainer pc{pts};
// dummy logic representing use possibilities
for (CartPoint& _pt : pc.GetIterator<CartPoint>())
{
_pt = CartPoint{1.0, 2.0} // modifies points in data via cartesian representation
}
// or
for (PolarPoint& _pt : pc.GetIterator<PolarPoint>())
{
_pt = PolarPoint{3.0, 4.0} // modifies points in data via polar representation
}
// after modification i can retrive vector in selected representation
std::vector<PolarPoint> polarRes = pc.Retrive<PolarPoint>();
std::vector<CartPoint> cartRes = pc.Retrive<CartPoint>();
return 0;
}
我将非常感谢有关设计此类 class 的任何建议或提出其他解决方案以解决对相同数据的双重表示需求的问题。
创建一个可以被视为CartPoint
或PolarPoint
的中间 class 可能是最惯用的。 您可以通过多种方式执行此操作:遏制和吸气剂,或 inheritance。 然后你可以存储这些点的向量。
CartPoint toCartPoint(const PolarPoint& pp) { ... }
CartPoint toPolarPoint(const CartPoint& cp) { ... }
class GenericPoint {
public:
GenericPoint(CartPoint cp)
: cp(cp), pp(toPolarPoint(cp)) {}
GenericPoint(PolarPoint pp)
: cp(toCartPoint(pp)), pp(pp) {}
GenericPoint(const GenericPoint& gp)
: cp(gp.cp), pp(gp.pp) {}
operator const CartPoint&() const { return cp; }
operator const PolarPoint&() const { return pp; }
const CartPoint& getCartPoint() const { return cp; }
const PolarPoint& getPolarPoint() const { return pp; }
GenericPoint& operator=(const GenericPoint& rhs)
{
cp = rhs.cp;
pp = rhs.pp;
}
GenericPoint& operator=(const CartPoint& cp_in)
{
cp = cp_in;
pp = toPolarPoint(cp_in);
}
GenericPoint& operator=(const PolarPoint& pp_in)
{
cp = toCartPoint(pp_in);
pp = pp_in;
}
private:
CartPoint cp;
PolarPoint pp;
};
我建议选择其中一个,仅在绝对需要时转换为另一个。 如果您在 select 内部存储CartPoint
s,如果某些计算需要PolarPoints
,您可以在CartPoint
s 和PolarPoint
s 之间来回转换。 不过,您std::vector<CartPoint>
存储在您的容器中。
请记住,浮点数学很棘手,如果您在PolarPoint
域中进行计算并转换为CartPoint
,就好像您在CartPoint
域中进行计算一样,您可能不会得到相同的结果。
尽管如此,它可能看起来像这样:
struct CartPoint {
double x = 0.;
double y = 0.;
CartPoint() = default;
CartPoint(double X, double Y) : x{X}, y{Y} {}
// convert a PolarPoint to a CartPoint
CartPoint(const PolarPoint& pp) :
CartPoint(pp.r * std::cos(pp.alpha),
pp.r * std::sin(pp.alpha))
{}
// comparisons
auto operator<=>(const CartPoint& rhs) const = default;
// return a Length proxy object in case the length is only used for comparisons
Length length() const { return {x*x + y*y}; }
// convert to a PolarPoint if needed by some function
operator PolarPoint () const {
return {std::sqrt(x*x+y*y), std::atan2(y, x)};
}
};
当我们只想比较两个CartPoint
的长度时, Length
代理 object 可以帮助我们避免调用std::sqrt
:
struct Length {
double len_squared;
// don't use sqrt if only comparing lengths
auto operator<=>(const Length& rhs) const {
return len_squared <=> rhs.len_squared;
}
// comparisons with actual doubles
auto operator<=>(double l) const {
return len_squared <=> l*l; // square the supplied value
}
// convert to double only when the actual length is needed
operator double() const { return std::sqrt(len_squared); }
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.