我正在学习C ++(以及一般的编程),我正在尝试同时创建Point类和Line类。
一条线应该由2个点对象组成。
C ++专家可以查看我的工作并告诉我这是否应该如何恰当地使用指针,引用和类?
class Point
{
private:
int x, y;
public:
Point() : x(0), y(0) {}
Point(int x, int y) : x(x), y(y) {}
}
class Line
{
private:
Point *p1;
Point *p2;
public:
Line(Point &p1, Point &p2) : p1(p1), p2(p2) {}
void setPoints(Point &p1, Point &p2)
{
this->p1 = p1;
this->p2 = p2;
}
}
答案 0 :(得分:7)
您的代码中根本不应该使用指针。使用实际对象。实际上,在C ++中很少使用指针。
class Point
{
private:
int x, y;
public:
Point() : x(0), y(0) {}
Point(int x, int y) : x(x), y(y) {}
}
class Line
{
private:
Point p1;
Point p2;
public:
Line(const Point & p1, const Point & p2 ) : p1(p1), p2(p2) {}
void setPoints( const Point & ap1, const Point & ap2)
{
p1 = ap1;
p2 = ap2;
}
}
答案 1 :(得分:5)
+1 zabzonk说的话。
使用指针的时间,如你所使用的那样,将是:
如果行包含指向现有点的指针,则可以实现上面的步骤3。它引入了复杂性(例如,“当你销毁一个Point实例时,包含指向Point的指针的Line实例会发生什么?”),当(如zabzonk建议)每个Line包含它自己的Point值时,它不存在。 / p>
答案 2 :(得分:5)
您的行类的Point成员是否是指针会创建一个非常不同类型的类。使用指针将产生经典的CoGo风格方法,可以将其视为像板中钉子的点,线条是连接这些钉子的橡皮筋。改变一个点就像移动钉子一样,所有相关的线条都会自动跟随,这在某些应用中是理想的。
使用文字点意味着这些行彼此独立,这适用于其他类型的应用程序。
在此阶段,这是您班级的关键设计决策。
编辑如其他帖子和下面的评论中所述,利用简单的指针实现多个线和点之间的关联也会带来严重的潜在问题。具体来说,如果在内存中删除或移动一个点,则必须更新引用该点的所有指针。实际上,通过使用唯一的点ID来查找点而不是简单的指针,可以克服这种情况。这样也可以轻松地序列化/保存CoGo结构。因此,我们的point类将有一个静态成员来获取基于ID的点,而我们的行类将有两个点ID而不是指针。
答案 3 :(得分:2)
我更喜欢这个......
class Point
{
private:
int x, y;
public:
Point() : x(0), y(0) {}
Point(int x, int y) : x(x), y(y) {}
}
class Line
{
private:
Point p1;
Point p2;
public:
Line(const Point &p1, const Point &p2) : p1(p1), p2(p2) {}
void setPoints(const Point &p1, const Point &p2)
{
this->p1 = p1;
this->p2 = p2;
}
}
答案 4 :(得分:2)
不需要在Line类中使用指针。
此外,以下行不正确:
Line(Point &p1, Point &p2) : p1(p1), p2(p2) {}
为什么呢?您正在将Point &
(p1)分配给Point *
(Line :: p1),这是非法的。你想要那里的指针。
您的Point类无法修改其数据。不太有用......
对我来说,Line类看起来像这样:
class Line
{
private:
Point p1, p2;
public:
Line()
{
}
Line(Point start, Point end) :
p1(start), p2(end)
{
}
const Point &startPoint() const
{
return p1;
}
Point &startPoint()
{
return p1;
}
const Point &endPoint() const
{
return p2;
}
Point &endPoint()
{
return p2;
}
};
您现在可以像这样编辑自己的行:
Line line;
line.startPoint() = Point(4, 2);
line.endPoint() = Point(6, 9);
答案 5 :(得分:1)
我发现了一些事情:
答案 6 :(得分:1)
我认为制作Point的指针没什么价值(除了讽刺价值)。你的Point在32位系统上占用8个字节(2个int)。指针需要4个字节。你节省了4个字节。
至于正确性,你的Line构造函数接受引用,但是你将它们分配给指针。这甚至不应该编译。你也在setPoints中做同样的事情。最好简单地将两个点作为实际对象并复制它们的值。
答案 7 :(得分:0)
class Line
{
private:
Point *p1; /* No memory allocated */
Point *p2; /* No memory allocated */
public:
Line(Point &p1, Point &p2) : p1(p1), p2(p2) {}
void setPoints(Point &p1, Point &p2) /* passed references to Point objects */
{
this->p1 = p1; /* asssiging Point objects to Point *! */
this->p2 = p2; /* asssiging Point objects to Point *! */
}
}
setPoints()函数不起作用 - 乍一看。 它应该是
void setPoints(Point &p1, Point &p2)
{
this->p1 = &p1; /* asssiging Point objects to Point *! */
this->p2 = &p2; /* asssiging Point objects to Point *! */
}
然后,我们无法控制p1和p2何时被破坏。最好使用p1和p2中的数据创建this-> p1和this-> p2,以便析构函数可以控制内存
答案 8 :(得分:0)
编译器会在此代码中给出错误:
我认为结束了语法。
代码还有另一个问题:
Line的p1和p2类成员是指向Point实例的指针。谁创建了这些实例,谁将在不再需要它们时将其删除?如果您按原样设计类,则创建Line实例的代码会将两个Point实例传递给构造函数。如果在Line之前删除了这些Point实例,则Line会留下两个悬空指针(不再引用有效实例的指针)。
此外,现在可以从Line类之外的代码修改现在属于Line的两个Point实例。这可能导致非常不利的情况。
在这种情况下,我会将p1和p2成员声明为Point(而不是指向Point的指针)。这样,传递给构造函数的Point实例将复制到类成员。只要Line存在,Point成员就会存在。它们只能由行类本身改变。
答案 9 :(得分:0)
在询问语言大师的意见之前,开始考虑设计,在这种情况下尤其是关于对象的生命周期:是否需要在没有线条的情况下存在?线共享积分?谁创造了一个点,什么时候停止存在?具有相同坐标的两个点是相同的,还是相等的(一个可能是红色,另一个可能是蓝色)? ...
看来大多数人都同意你应该在如此小的代码库上使用值语义。有时,问题需要许多方面引用相同的对象(即Point),然后使用指针(或引用)。
指针和引用之间的选择仍然是另一回事。我更喜欢在实现聚合时使用引用(没有它的端点就不能存在一行),以及在实现较少“亲密”关系时使用指针。
答案 10 :(得分:0)
在Line类中使用Point对象。点数的所有权不明确,你可能会因为悬空指针或泄漏的内存而冒险。
您的默认构造函数是一个问题,因为您很少想在(0,0)创建一个Point。最好将默认的x,y值设置为MAXINT,并声明任何使用Point都没有MAXINT作为其值之一。有一个is_valid()函数,以便客户端可以测试。您的Line类还可以具有其两点无效的前提条件。不允许有效点具有MAXINT值的回报是,您可以检测到Point未正确初始化的时间,并且您将删除一些难以找到类使用中的错误。