首页 文章

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

提问于
浏览
2

Effective C++对"Prefer non-member non-frend functions to member functions"说(第23项) . 理由对我来说很有意义:它最小化了API "surface area" . 但在实践中,我经常发现很难说服人们(包括我自己)遵循 . 例如,假设我有一些形状类,它们应该支持周长和面积计算:

// @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应该是非成员非朋友函数(不是方法),因为它们可以 . 例如 . 可以从width和height方法计算Rectangle的区域,如下所示:

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

    如果你有一个带有一些可以通过getter和setter直接访问的变量的类,你有什么?是的,没有多少或少于 struct 与"object oriented"门面 . 对于这种数据容器的使用,绝对没有任何面向对象 .

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

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

  • 5

    如果您需要计算多态 Shape 对象的区域,那么Scott 's advice is not for your case. Because you cannot calculate the area with an external function, because in fact you don' t可以公开访问所需的信息 . 即,对象实际上是圆形,矩形还是其他东西 . 所以这是虚拟功能的工作 .

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

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

相关问题