变形还是封装,这就是问题! (C++)

发布于 2024-11-25 03:06:55 字数 1026 浏览 2 评论 0 原文

我需要将多态对象(假设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); }

但这意味着我没有多态性...

显然我在这里缺少一些东西...干杯!

I need to store a polymorphic object (let's say Polygon) inside another object (let's say Simulation). At the same time I want to keep encapsulation of 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;
}

This works perfectly fine! However, it violates encapsulation of Simulation, in fact, if from the main I go and change square it will also change inside Simulation.

I was thinking of modifying the constructor of Simulation using the copy constructor as:

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

but this will mean that I don't have polymorphism...

There is obviously something I am missing here... CHEERS!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

橘虞初梦 2024-12-02 03:06:55

向 Polygon 添加克隆函数(以及虚拟析构函数!)。确保 Polygon 是抽象的是一个好主意,因此确保至少一个函数是纯虚函数。

您的模拟类将需要复制构造函数、析构函数和赋值运算符。
请注意,即使超类返回 Polygon*,Square 克隆函数也可以返回 Square*,因为它是协变的。一些较旧的编译器可能不支持此功能,在这种情况下返回 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_;
};

Add a clone function to Polygon (and a virtual destructor!). It is a good idea to ensure that Polygon is abstract so make sure at least one function is pure virtual.

Your Simulation class will require a copy constructor, destructor and assignment operator.
Note that the Square clone function can return a Square* even though the super class returns a Polygon* because it is covariant. Some older compilers may not support this, in which case return a 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_;
};
烦人精 2024-12-02 03:06:55

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

所以,要么:

outside -> polygon -> callback -> simulation 

要么

outside -> simulation -> polygon

If Simulation contains Polygon then it means that it is meant to do something with it. If you need to access the polygon directly from the 'outside', you have either missed the design somewhere, or if not, you can use observer pattern and have polygon notify the simulation if something about it changes.

So, either:

outside -> polygon -> callback -> simulation 

or

outside -> simulation -> polygon
那一片橙海, 2024-12-02 03:06:55

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

您可以考虑一个抽象复制方法来实现这一点:(不要忘记在模拟析构函数中删除)

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()) {}
};

So you want to make sure that there's no way for outside code to alter the inner polygon of simulation, but yet allow any subclass to be used inside it? I.e. make sure that there are no references outside of simulation to the object passed by ref in the c'tor?

You could think of an abstract copy method to accomplish that: (don't forget to delete in simulation destructor)

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()) {}
};
指尖凝香 2024-12-02 03:06:55

如果要复制多态对象,可以使用克隆方法来完成。

class Polygon
{
  ...
  virtual Polygon* clone() const = 0;
};

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

然而,在该示例中,模拟既不对多边形本身执行任何操作,也不想将其分发给其他代码使用,这似乎有点毫无意义。

If you want to copy a polymorphic object, this can be done with a clone method.

class Polygon
{
  ...
  virtual Polygon* clone() const = 0;
};

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

However, in the example it seems a bit pointless that the Simulation neither does anything with the polygon itself nor do you want to hand it out for other code to use.

浪菊怪哟 2024-12-02 03:06:55

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

That's just how C++ works. If you write a wrapper for an object (PIMPL) you have to implement its full interface. The functions going to be very small just passing the calls to the actual implementation but you have to write them. Then you can alter the behaviour, add logging, or whatever you need...

番薯 2024-12-02 03:06:55

您只需要确定多边形是在模拟内部还是外部。如果它应该在它之外,那么您就有引用构造函数参数。如果它在里面,您将需要以下代码:

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

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

You just need to decide if the polygon is inside or outside of simulation. If it's supposed to be outside of it, then you have reference constructor parameter . If it's inside, you'll need the following code:

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

Now, the polymorphism aspect you can easily do like this:

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

And once you get tired to adding new data members, here's another trick which will fix some cases:

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

Edit: Note that the order of classes in header file needs to be carefully considered.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文