如何选择会员和非会员功能?

时间:2013-08-24 14:37:28

标签: c++

Effective C++说“首选非成员非frend函数到成员函数”(第23项)。理由对我来说很有意义:它最小化了API“表面积”。但在实践中,我经常发现很难说服人们(包括我自己)效仿。例如,假设我有一些形状类,它们应该支持周长和面积计算:

// @interface
class Shape {
 public:
  virtual double Area() = 0;
  virtual double Perimeter() = 0;
}

class Rectangle : public Shape {
 public:
  Rectangle(double width, double height);

  double width();
  double height();

  ...
};

class Circle : public Shape {
 public:
  Circle(double radius);

  double radius();
  ...
};

根据这个建议,似乎Area和Perimeter应该是非成员非朋友函数(不是方法),因为它们可以。例如。可以从宽度和高度方法计算矩形区域,如下所示:

double Area(const Rectangle& rectangle) {
  return rectangle.width() * rectangle.height();
}

事实上,Rectangle和Circle都没有任何内部状态,它们的吸气剂都没有暴露,而且很难想象会是怎样的。因此,对这些操作的任何函数都不应该是一种方法。另一个例子:

// The diameter of a shape is the (circle) diameter of the smallest circle
// that contains a shape.
double Diameter(const Rectangle& rectangle) {
  double w = rectangle.width();
  double h = rectangle.height();
  return sqrt(w * w + h * h);
}

我在这里遗漏了什么吗?或者这实际上是不好的建议??

2 个答案:

答案 0 :(得分:5)

如果你需要计算多态Shape对象的面积,那么斯科特的建议不适合你的情况。因为您无法使用外部函数计算区域,因为实际上您无法公开访问所需的信息。即,对象实际上是圆形,矩形还是其他东西。所以这是虚拟功能的工作。

事实上,在Scott的psuedo-code算法中,用于确定函数的正确放置(取自this article,因为我没有这本书),第一个测试就是:

if (f needs to be virtual)
    make f a member function of C;

答案 1 :(得分:0)

如果你的课程有一些可以通过getter和setter直接访问的变量,你有什么?是的,没有多于或少于struct具有“面向对象”的外观。对于使用这样的 data 容器,绝对没有任何面向对象。

关于面向对象的全部观点,正如我所说,是模拟行为,而不是数据。在这方面,使用纯虚方法计算面积和周长的抽象形状类是一种完全有效的设计:它从数据中抽象出来并暴露您需要的行为。如果我是你,我会考虑为基本参数添加getter(没有有用的抽象),并且添加相应的setter三次(打破封装)。

但是,无论如何,不​​要因为拥有访问者,或者没有访问者,或者避免简单的旧数据结构,甚至是面向对象的设计而抱怨。所有这些都有它的用途,并且为了避免它而避免它会导致在一种或另一种情况下设计不良。我所说的只是:思考什么最适合你的需求,然后完全无视一些大程序员提出的宗教规则。