简体   繁体   English

编程风格:object 带有引用或值的段落? (c++)

[英]Programming style: object with passages by references or value? (c++)

This is a general question on programming style.这是关于编程风格的一般问题。 Let's say I have an object Line which has some methods and private variables Point point_a_ and Point point_b_ .假设我有一条 object Line ,它有一些方法和私有变量Point point_a_Point point_b_ Let's say that at some point I need to change the position of the two points.假设在某些时候我需要更改两点的 position。 What programming style would you prefer between the following cases?在以下情况下,您更喜欢哪种编程风格? They all do the same thing (or should do: I didn't compile, but seems pretty straightforward).他们都做同样的事情(或者应该做:我没有编译,但看起来很简单)。

CASE 1情况1

Class Line {
public:
  Line(Point point_a, Point point_b) : point_a_(point_a), point_b_(point_b) {}

  void UpdatePoints(Point point_a, Point point_b) { 
    point_a_ = point_a; point_b_ = point_b;
  }

  double Distance();

private:
  Point point_a_;
  Point point_b_;
};

int main (int argc, char * const argv[]) {
  Point point_a(0,0,0);
  Point point_b(1,1,1);
  Line line(point_a,point_b);
  std::cout<<line.Distance()<<"\n";

  point_a.x = 1;
  line.UpdatePoints(point_a,point_b);
  std::cout<<line.Distance()<<"\n";
}

CASE 2案例二

Class Line {
public:
  Line(Point point_a, Point point_b) : point_a_(point_a), point_b_(point_b) {}
  Point& point_a() { return point_a_; }
  Point& point_b() { return point_b_; }
  double Distance();

private:
  Point point_a_;
  Point point_b_;
};

int main (int argc, char * const argv[]) {
  Point point_a(0,0,0);
  Point point_b(1,1,1);
  Line line(point_a,point_b);
  std::cout<<line.Distance()<<"\n";

  line.point_a().x = 1;
  std::cout<<line.Distance()<<"\n";
}

CASE 3案例 3

Class Line {
public:
  Line(Point* point_a, Point* point_b) : point_a_(point_a), point_b_(point_b) {}

  double Distance();

private:
  Point* point_a_;
  Point* point_b_;
};

int main (int argc, char * const argv[]) {
  Point point_a(0,0,0);
  Point point_b(1,1,1);
  Line line(&point_a,&point_b);
  std::cout<<line.Distance()<<"\n";

  point_a.x = 1;
  std::cout<<line.Distance()<<"\n";
}

Any feedback is greatly appreciated!!非常感谢任何反馈!

Thanks!谢谢!

[EDIT] Speed is paramount in my software! [编辑] 速度在我的软件中是最重要的!

In this simple scenario I might just use public member variables.在这个简单的场景中,我可能只使用公共成员变量。

Otherwise I would provide getters that return a const reference and matching setters.否则,我将提供返回const 引用和匹配设置器的 getter。

class Line {
public:
    Line(const Point& p1, const Point&p2) : m_p1(p1), m_p2(p2) {}

    const Point& p1() const 
    { return m_p1; }

    const Point& p2() const
    { return m_p2; }

    void setP1(const Point& p1)
    { m_p1 = p1; }

    void setP2(const Point& p2)
    { m_p2 = p2; }

private:
    Point m_p1;
    Point m_p2;
};

Case three is totally out because it completely violates principles of encapsulation.案例三完全被淘汰了,因为它完全违反了封装原则。 Case two does as well, to a slightly lesser extent.案例二也有,只是程度稍小一些。 I would prefer option one, but did you consider possibly making the points immutable and forcing you to create a new object when it changes?我更喜欢选项一,但您是否考虑过可能使这些点不可变并在它发生变化时强迫您创建一个新的 object?

Also to be pedantic if I remember correctly from many years ago, a line technically extends infinitely in both directions.如果我多年前没记错的话,也是迂腐的,从技术上讲,一条线在两个方向上无限延伸。 You're actually representing a line segment .您实际上是在代表一条线段

Case 2 is no better than public member variables.情况 2 并不比公共成员变量好。 In particular, it doesn't encapsulate anything.特别是,它不封装任何东西。

Case 3 makes ownership of the points unclear.案例 3 使点的所有权不清楚。 Consider what happens if your points are local variables in the calling function, and then they go out of scope.考虑如果您的点是调用 function 中的局部变量,然后它们是 go 出 scope 会发生什么。 It also offers no benefits over public member variables.与公共成员变量相比,它也没有任何好处。

So out of these three options, Case 1 is the cleanest, IMO.所以在这三个选项中,案例 1 是最干净的,IMO。 Other options are:其他选项包括:

  • simply use public member variables.只需使用公共成员变量。
  • make Line immutable.使Line不可变。
  • use set and get functions.使用setget函数。

I would choose either case 1 or an immutable Line class.我会选择案例 1 或不可变线 class。

Case 2 allows changes to the Point objects without the knowledge of their containing line.情况 2 允许在不知道包含线的情况下更改 Point 对象。 At some point, you might need the line to know if its points have been changed.在某些时候,您可能需要这条线知道它的点是否已更改。

Case 3 either makes the Line object dependent on the lifetime of the Points, or makes the Line the owner of the points, which is not clear from the API.案例 3 要么使线 object 依赖于点的生命周期,要么使线成为点的所有者,这在 API 中并不清楚。

An immutable Line would allow you to create a new Line object with new points.一条不可变的 Line 将允许您创建一条带有新点的新 Line object。

Case 4 -- Prefer passing by const reference rather than value (or pointer):案例 4 - 更喜欢通过 const 引用而不是值(或指针)传递:

class Line
{
public:
    Line(const Point& a, const Point& b) : a_(a), b_(b)
    {}

    const Point& get_a() const { return a_; }
    const Point& get_b() const { return b_; }

    void set_a(const Point& a) { a_ = a; }
    void set_b(const Point& b) { b_ = b; }

private:
    Point a_;
    Point b_
};

This is better in that in enforces encapsulation - the only way to change the variables held in the class after construction is a specific mutator method.这样做更好,因为强制封装 - 在构造后更改 class 中保存的变量的唯一方法是特定的 mutator 方法。

The accessors return a const reference, so they cannot be modified (copies made from these can be).访问器返回一个 const 引用,因此它们不能被修改(可以从它们复制)。

The class as a whole is made const-correct. class 作为一个整体是 const 正确的。

References are arguably better than pointers in this instance because they are guaranteed (unless you specifically break that guarantee) to not be NULL.在这种情况下,引用可以说比指针更好,因为它们被保证(除非你特别打破这个保证)不是 NULL。

Consider another option:考虑另一种选择:

class Segment {
public:
    Segment(Point point_a, Point point_b);
    Point point_a() const;
    Point point_b() const;

private:
   Point point_a_;
   Point point_b_;
};


double Distance( Segment seg );

int main (int argc, char * const argv[]) {
    Point point_a(0, 0, 0);
    Point point_b(1, 1 ,1);
    Segment seg(point_a, point_b);
    std::cout << Distance(seg) << "\n";

    point_a.x = 1;
    seg = Segment(point_a, point_b); // reset
    std::cout << Distance(seg) << "\n";
}

I used the name Segment as per suggestion above.根据上面的建议,我使用了名称 Segment。 This style is closer to functional programming style.这种风格更接近函数式编程风格。 Segment is immutable unless you explicitly reset it with a common assignment syntax. Segment 是不可变的,除非您使用常见的赋值语法显式重置它。 Distance is not a member function because it can be implemented in terms of Segment's public interface. Distance 不是成员 function 因为它可以在 Segment 的公共接口方面实现。

Regards, &rzej问候, &rzej

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM