编程风格:对象与引用或值通道?(c++)

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

本文关键字:通道 c++ 风格 对象 引用 编程      更新时间:2023-10-16

这是一个关于编程风格的一般性问题。假设我有一个对象Line,它有一些方法和私有变量Point point_a_Point point_b_。假设在某一点上我需要改变这两点的位置。在以下情况中,您更喜欢哪种编程风格?它们都做同样的事情(或者应该做的事情:我没有编译,但看起来很简单)。

案例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";
}

案例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";
}

情况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";
}

任何反馈都非常感谢!!

谢谢!

[EDIT]在我的软件中速度是最重要的!

在这个简单的场景中,我可能只使用公共成员变量。

否则我会提供返回const引用的getter和匹配的setter。

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;
};

第三种情况完全不适用,因为它完全违反了封装原则。情形二也一样,只是程度稍小一些。我更喜欢选项一,但你有没有考虑过让点不可变,并在它改变时强迫你创建一个新对象?

如果我多年前没记错的话,这也是一种迂腐,从技术上讲,一条线在两个方向上无限延伸。您实际上表示的是一行

情况2不比公共成员变量好。特别是,它不封装任何东西。

情形3使得点的所有权不明确。考虑一下,如果你的点是调用函数中的局部变量,然后它们超出了作用域,会发生什么。与公共成员变量相比,它也没有提供任何好处。

所以在这三个选项中,我认为情况1是最干净的。其他选项包括:

  • 直接使用公共成员变量。
  • 使Line不可变
  • 使用setget函数

我会选择case 1或不可变的Line类。

Case 2允许在不知道其包含行的情况下更改Point对象。在某些时候,您可能需要这条线知道它的点是否已经改变。

情况3要么使Line对象依赖于点的生存期,要么使Line成为点的所有者,这在API中是不清楚的。

不可变的Line允许你用新的点创建一个新的Line对象

Case 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_
};

这是更好的,因为它强制封装——在构造之后改变类中保存的变量的唯一方法是一个特定的mutator方法。

访问器返回一个const引用,因此它们不能被修改(可以从它们复制)。

整个类都是const-correct。

在这种情况下,

引用比指针更好,因为它们被保证不为NULL(除非你特别打破了这个保证)。

考虑另一个选项:

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";
}

我按照上面的建议使用了名称Segment。这种风格更接近函数式编程风格。段是不可变的,除非你用通用赋值语法显式地重置它。Distance不是成员函数,因为它可以通过Segment的公共接口实现。

问候,div, rzej