Чтобы полиморф или инкапсулировать, это вопрос!(C ++) - PullRequest
2 голосов
/ 20 июля 2011

Мне нужно хранить полиморфный объект (скажем, 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); }

но это будет означать, что у меня нет полиморфизма ...

Очевидно, что-то здесь мне не хватает ... ПРИВЕТ!

Ответы [ 6 ]

3 голосов
/ 20 июля 2011

Добавьте функцию клонирования в 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_;
};
1 голос
/ 20 июля 2011

Итак, вы хотите убедиться, что внешний код не может изменить внутренний полигон симуляции, но при этом разрешить использовать в нем любой подкласс?Т.е. убедитесь, что нет никаких ссылок вне симуляции на объект, переданный ref в c'tor?

Вы могли бы подумать о методе абстрактного копирования, чтобы выполнить это: (не забудьте 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()) {}
};
1 голос
/ 20 июля 2011

Если Simulation содержит Polygon, то это означает, что оно должно с ним что-то делать. Если вам нужен прямой доступ к полигону «извне», вы либо пропустили дизайн, либо, если нет, можете использовать шаблон наблюдателя и попросить полигон уведомить симуляцию, если что-то изменится.

Итак, либо:

outside -> polygon -> callback -> simulation 

или

outside -> simulation -> polygon
0 голосов
/ 20 июля 2011

Так работает C ++.Если вы пишете обертку для объекта (PIMPL), вы должны реализовать ее полный интерфейс.Функции будут очень маленькими, просто передавая вызовы фактической реализации, но вы должны написать их.Затем вы можете изменить поведение, добавить ведение журнала или все, что вам нужно ...

0 голосов
/ 20 июля 2011

Если вы хотите скопировать полиморфный объект, это можно сделать с помощью метода клонирования.

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

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

Однако в примере кажется немного бессмысленным, что Simulation также ничего не делает с самим полигоном.и вы не хотите раздавать его для использования другим кодом.

0 голосов
/ 20 июля 2011

Вам просто нужно решить, находится ли полигон внутри или снаружи симуляции. Если он должен быть вне его, то у вас есть параметр ссылочного конструктора. Если он внутри, вам понадобится следующий код:

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

Изменить: Обратите внимание, что порядок классов в заголовочном файле должен быть тщательно продуман.

...