是变形还是被封装,这是一个问题!(c++)

To polymorph or to incapsulate, this is the question! (C++)

本文关键字:一个 变形 问题 c++ 封装      更新时间:2023-10-16

我需要将多态对象(假设Polygon)存储在另一个对象(假设Simulation)中。同时我想保持Simulation的封装。

class Polygon {
public:
  virtual double area() { return 0; }
};
class Square : public Polygon {
public:
  Square(double edge) : edge_(edge) {}
  virtual double area() { return edge_*edge_; }
private:
  double edge_;
};
class Simulation {
public:
  Simulation(Polygon& polygon) { polygon_ = &polygon; }
  Polygon* polygon() { return polygon_; }
private:
  Polygon* polygon_;
};
int main (int argc, const char * argv[]) {
  Square square(2.0);
  Simulation sim(square);
  std::cout<<sim.polygon()->area()<<"n";
  return 0;
}

这工作得很好!然而,它违反了Simulation的封装,事实上,如果我从main中去改变square,它也会在Simulation中改变。

我正在考虑使用复制构造函数修改Simulation的构造函数:

Simulation(Polygon& polygon) { polygon_ = new Polygon(polygon); }

但是这将意味着我没有多态性…

显然我在这里错过了什么…干杯!

给Polygon添加一个克隆函数(和一个虚拟析构函数!)这是一个好主意,确保多边形是抽象的,所以确保至少有一个函数是纯虚的。

您的Simulation类将需要复制构造函数、析构函数和赋值操作符。注意,Square克隆函数可以返回一个Square*,即使超类返回一个Polygon*,因为它是协变的。一些旧的编译器可能不支持这个,在这种情况下返回一个Polygon*.

class Polygon {
public:
  virtual ~Polygon() = 0;
  virtual Polygon* clone() const = 0;
  virtual double area() { return 0; }
};
inline Polygon::~Polygon() {}
class Square : public Polygon {
public:
  Square(double edge) : edge_(edge) {}
  virtual Square* clone() const { return new Square(*this); }
  virtual double area() { return edge_*edge_; }
private:
  double edge_;
};
class Simulation {
public:
  Simulation(Polygon const& polygon) 
  : polygon_(polygon.clone())
  {}
  Simulation(Simulation const& rhs) 
  : polygon_(rhs.polygon_->clone())
  {}
  Simulation& operator=(Simulation const& rhs) 
  {
      if (this != &rhs) {
          delete polygon_;
          polygon_ = rhs.polygon_->clone();
      }
      return *this;
  }
  ~Simulation() {
     delete polygon_;
  }
  Polygon* polygon() { return polygon_; }
private:
  Polygon* polygon_;
};

如果Simulation包含Polygon,那么这意味着它意味着要做一些事情。如果你需要直接从"外部"访问多边形,你要么错过了设计的某个地方,或者如果没有,你可以使用观察者模式,并让多边形通知模拟,如果它的某些变化。

:

outside -> polygon -> callback -> simulation 

outside -> simulation -> polygon

所以你想要确保外部代码无法改变模拟的内部多边形,但却允许在其中使用任何子类?即确保在c'tor中ref传递的对象的模拟之外没有引用?

你可以想到一个抽象的复制方法来完成:(不要忘记在模拟析构函数中添加delete)

class Polygon {
public:
   virtual Polygon *copy() = 0;
   //..
};
class Square : public Polygon {
public:
   virtual Polygon *copy() { return new Square(_edge); }
   //...
}
class Simulation {
public:
   Simulation(const Polygon &p) : poly(p.copy()) {}
};

如果你想复制一个多态对象,这可以用clone方法来完成。

class Polygon
{
  ...
  virtual Polygon* clone() const = 0;
};
class Square: public Polygon
{
  ...
  virtual Square* clone() const { return new Square(*this); }
};

然而,在这个例子中,模拟既没有对多边形本身做任何事情,也不想把它交给其他代码使用,这似乎有点毫无意义。

这就是c++的工作原理。如果为对象编写包装器(PIMPL),则必须实现其完整的接口。这些函数非常小,只是将调用传递给实际实现,但你必须编写它们。然后你可以改变行为,添加日志记录,或任何你需要的…

你只需要决定这个多边形是在模拟的内部还是外部。如果它应该在它的外部,那么你有引用构造函数参数。如果它在内部,则需要以下代码:

class Simulation {
public:
    Simulation() : poly(2.0) { }
    Polygon *polygon() { return &poly; }
private:
    Square poly;
};

现在,多态性方面可以很容易地这样做:

class Simulation {
public:
    Simulation() : poly(2.0), poly2(3.0) { }
    Polygon *polygon(int i) 
   { 
       switch(i) { 
          case 0: return &poly;  
          case 1: return &poly2; 
       } 
       return 0; 
   }
private:
    Square poly;
    Cylinder poly2;
};

一旦你厌倦了添加新的数据成员,这里有另一个技巧可以解决某些情况:

class Simulation {
public:
    Simulation() : poly(2.0) { }
    Polygon *polygon(float x) 
    {
        poly.edge_ = x;
        return &poly;
    }
private:
    Square poly;
 };

编辑:注意头文件中类的顺序需要仔细考虑