作为C ++的初学者,我正在练习带有算法分配的C ++。一路上,我有一些问题,我很难通过。请原谅我,因为我还在学习,所以问题听起来是入门级的。
目标是在给定的三个类的矢量点中找到共线点。以下简要介绍三个类别'用途:
所以,我希望客户端代码看起来像这样:
std::vector<Point> points; // may become huge
// populate points
// ...
Collinear collinear_points(points);
std::vector<LineSegment> segments_in_points = collinear_points.GetSegments();
由于共线类依赖于某个点的矢量来相应地获得这些段,我认为它需要它作为数据成员。一直困扰着我的问题是,如果持有向量的副本或持有对象外的向量的原始指针/引用。我认为智能指针在这里会有点过分。根据旧答案here,也许最好是参考,这也避免了潜在的昂贵复制?如果存在类,那么类之间存在这种依赖关系的常见做法是什么?
如果在构建 collinear_points 后修改点,则引用的数据 collinear_points 将与其包含的段不一致。是否通常将责任留给用户以确保对象的有效性取决于其他对象?有没有办法让 collinear_points 知道内容已被修改并将其置于无效状态?
答案 0 :(得分:1)
从标题中回答你的实际问题:非拥有的原始指针仍然是通常的选择,主要是因为这是我们自旧C日以来一直在做的事情。当然,指针有问题,它可以是nullptr
。因此,使用引用可以更清楚地传达null不是允许的值。因为我倾向于使用参考,虽然它甚至对我自己感觉有点奇怪。但总的来说,这是更好的设计决策
那就是说,我认为这里真正的问题是所有权。如果Collinear
不拥有该向量,则API的用户必须确保该向量至少与关联的Collinear
对象一样长。否则你将访问一个悬空指针/参考,事情往往会从那里下坡。 ;)
有没有办法让collinear_points知道内容已被修改并将其置于无效状态?
是的,有。 拥有一切。包括点向量和段向量。遵循这种方法Collinear
看起来像这样:
class Collinear {
public:
// usings because I’m a lazy typer
using PointsVec = std::vector<Point>;
using SegmentsVec = std::vector<LineSegment>;
// Take ownership of the points vector by either copying
// or moving it into a member.
explicit Collinear(const PointsVec& p): m_points(p) {}
explicit Collinear(PointsVec&& p): m_points(std::move(p)) {}
// Retain ownership of the segments vector by returning a const ref.
const SegmentsVec& GetSegments(); // Check if you can make it const.
// access functions for the two vectors ...
private:
PointsVec m_points;
SegmentsVec m_segments;
}
现在Collinear
控制对点向量的访问。您必须将允许操作的函数编写为Collinear
的成员。重要的是永远不要将非const指针或非const引用返回m_points
,因为那样你可能会错过写访问。
段矢量类似。您提供写访问成员函数并Collinear
保留所有权,这意味着它可以在必要时重新计算它,并且用户不需要关心它。根据计算的成本,您现在可以通过懒惰评估和您可以想到的每个优化来实现。
但是,有一种完全不同的设计方法。 什么都没有。 Collinear
是否必须是一个类?它可能是命名空间中的一堆免费函数吗?
namespace Collinear {
std::vector<LineSegment> GetSegments(const std::vector<Point>& points);
}
// ...
auto segments_in_points = Collinear::GetSegments(points);
这与自己的一切方法相反。以前,你有完全控制权。现在您的用户具有完全控制权。另一方面,他们现在必须处理任何懒惰/优化/更新检测。
哪种方法合适是a)API设计理念和b)您的混乱情况。你的用户是什么?他们期待什么?哪种方法让他们的生活更轻松?由于这是一项任务,您可能不会拥有任何真实用户。因此,想象一群人可能想要使用您的代码并根据它来决定。或者只是使用您将有更多乐趣实施的方法。重要的是imo:选择两种方法中的一种。不要混用它们,因为这样的API不一致。这会增加混乱,降低易用性,并且更容易出错。
顺便说一下:
我认为智能指针在这里会有点过分。
使用智能指针不是一个过度杀伤的问题。这是一个所有权问题。如果你有一个拥有指针从不使用原始指针。 ......除非传统的API强迫你。即使这样,将它标记为拥有像gsl::owner<T>
这样的透明包装也是一个好主意。
答案 1 :(得分:0)
一直困扰着我的问题是,它是否应该保存向量的副本或者保持对象外部向量的原始指针/引用
我认为你应该在这里保留一份vector来保持通用。你不希望你的collinear
类依赖于特定的vector<Points>
,而应该像创建实例时一样collinear
课程的vector<Points>
课程,您只需告诉它vector
,它必须处理。然后,如果您更改此collinear
,并且您希望collinear
也可以处理此数据集(您可能不需要),您有责任告知collinear
处理新数据集。如果您希望在更新vector<points>
时自动更新collinear
,则可以这样做,但是你必须回答一些问题,比如数据集发生变化时vector<points>
的状态(这将取决于{{1}})。