C ++ Проблема с наследованием кода оператора: требуется ли копировать один и тот же код для всех производных классов? - PullRequest
2 голосов
/ 13 января 2010

Я хотел бы написать группу класса D_I (он же I = 1,2, ... просто я имею в виду «_I» как собственное имя класса, а не целое число), которые обрабатывают некоторые (например, логические) операции F с одинаковым значением для всех классов. Но также я хочу оперировать «суммами» объектов таких классов, разные классы группы могут быть «сложены» вместе. Упомянутая операция суммирования такого объекта зависит от соответствующей операции добавления объектов.

Вот почему я собираюсь создать общий базовый класс B для обработки операции «суммирования»:

class B
  {public:
      B (): LEFT(NULL), RIGHT(NULL) {};
      B (const SpBrd& S);
      B (const B& A, const B B); 
      ~B ();
           {if (LEFT != NULL) delete LEFT; // NULL if it is default B or D_I
            if (RIGHT != NULL) delete RIGHT; // NULL if it is default B or D_I
           }
      B operator + (const B& S) const;
      bool F(const Point& P) const;
           {bool b = aF(P);
            if (LEFT != NULL) b = b && LEFT->F(P);  // this is how F depends 
            if (RIGHT != NULL) b = b && RIGHT->F(P);  // of added classes
           }
   protected:
      virtual bool aF(const Point& P) const {return true;}; // Default!
      B* LEFT; // Pointer to left operand of "sum" 
      B* RIGHT; // Pointer to right operand of "sum" 
         // (since it might point to D_i class too as well)
  };

таким образом, производный класс D легко написать, поскольку он должен обрабатывать только свой конструктор и aF:

class D_I: public B
   {public:
       D() {...};
    protected:
       virtual bool aF (const Point& P) const
           {// return something here
           }
   };

Проблема в следующем: КАК НАПИСАТЬ оператор + и скопировать конструктор класса B:

   B:B (const B& S)
       {if (S.LEFT != NULL) LEFT = new B (S.LEFT)
        if (S.RIGHT != NULL) RIGHT = new B (S.RIGHT)             
       };
   B B:operator + (const B& S) const
       {SpBrd S;
        S.LEFT = new B (*this);
        S.RIGHT = new B (S);
       };

не приведет к надлежащему результату, потому что 'new B ...' должен быть заменен на соответствующий 'new D_I ...', если 'this', 'S' в in 'operator +' или 'S.LEFT' или «S.RIGHT» в конструкторе копирования относится не к объекту класса B, а к объекту класса D_I.

Итак, я не могу найти способ, чтобы B :: operator + знал, какие типы источников имеют суммы, так как может быть большое количество D_I, и они будут время от времени добавляться. Что я должен сделать, чтобы написать все это правильно?

Я не могу написать пар операторы D_I + (D_J) и D_I :: D_I (const D_J) для всех пар (D_I, D_J), это нечестно!

(Это представляло бы что-то вроде границ (поверхности) в пространстве. Функция F представляет собой то, что находится внутри границы - то есть, чтобы проверить, находится ли точка внутри космического тела. Но тела внутри границы, которой я хочу управлять, могут быть пересечением ( оператор +) космических тел внутри двух других границ Примеры: пересечение двух границ сферы - внутренней и внешней зон - создает сферическую оболочку, также называемую трехмерным кольцом, может также потребоваться просмотр сферической границы пересечения с радиусом R и телесным углом 10 * 10 градусов вокруг полюса и т. д.)

Ответы [ 2 ]

3 голосов
/ 13 января 2010

Ваш вопрос сбивает с толку, и я чувствую себя здесь поспешно, но я скажу это.

Обычно операторы объявляются вне класса как функции, не являющиеся членами, и используют функции-члены для достижения своей цели. Это связано с тем, что оба аргумента оператора могут в равной степени участвовать в разрешении перегрузки.

В этом случае похоже, что вы пытаетесь создать какое-то дерево разбора на основе операторов C ++. В этом случае код, подобный этому, может быть тем, что вы ищете:

const B operator +(const B &a, const B &b)
{
    return B(a, b);
}

Обратите внимание, что это будет работать, даже если что-то, полученное из B, является либо операндом. И если я прав насчет того, что вы делаете, у вас, скорее всего, будет специальный тип узла для двух вещей, добавленных к каждому другому, и оператор + может вернуть это.


Теперь, когда я вижу вашу уточненную версию, вот другой ответ.

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

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

#include <memory>
#include <iostream>

template <class BaseType, class DerivedType>
class Cloneable {
 public:
   virtual ~Cloneable() {}

   ::std::auto_ptr<DerivedType> clone() const {
      return ::std::auto_ptr<DerivedType>(static_cast<DerivedType *>(i_clone()));
   }

 protected:
   virtual BaseType *i_clone() const {
      return new DerivedType(dynamic_cast<const DerivedType &>(*this));
   }
};

class A : public Cloneable<A, A> {
 public:
   A() {}
   A(const A &b) {
      const void * const voidb = &b;
      const void * const voidme = this;
      ::std::cerr << "Creating a copy of the A at " << voidb << " and this new copy will reside at " << voidme << "\n";
   };
   virtual ~A() {
      const void * const voidme = this;
      ::std::cerr << "Destroying the A at " << voidme << "\n";
   }
};

template <class Derived>
class B : public A, public Cloneable<A, Derived> {
 public:
   B() {}
   B(const B &b) {
      const void * const voidb = &b;
      const void * const voidme = this;
      ::std::cerr << "Creating a copy of the B at " << voidb << " and this new copy will reside at " << voidme << "\n";
   };
   virtual ~B() {
      const void * const voidme = this;
      ::std::cerr << "Destroying the B at " << voidme << "\n";
   }
   // Make sure clone can be mentioned in derived classes with no ambiguity
   using Cloneable<A, Derived>::clone;

 protected:
   // Force dominance rules to choose the correct i_clone virtual function.
   virtual A *i_clone() const {
      return Cloneable<A, Derived>::i_clone();
   }
};

class C : public B<C> {
 public:
   C() {}
   C(const C &b) {
      const void * const voidb = &b;
      const void * const voidme = this;
      ::std::cerr << "Creating a copy of the C at " << voidb << " and this new copy will reside at " << voidme << "\n";
   };
   virtual ~C() {
      const void * const voidme = this;
      ::std::cerr << "Destroying the C at " << voidme << "\n";
   }
};

class D : public B<D> {
 public:
   D() {}
   D(const D &b) {
      const void * const voidb = &b;
      const void * const voidme = this;
      ::std::cerr << "Creating a copy of the D at " << voidb << " and this new copy will reside at " << voidme << "\n";
   };
   virtual ~D() {
      const void * const voidme = this;
      ::std::cerr << "Destroying the D at " << voidme << "\n";
   }
};

int main(int argc, const char *argv[])
{
   C c;
   D d;
   ::std::auto_ptr<A> cptr(c.clone());
   ::std::auto_ptr<A> dptr(d.clone());
   cptr = dptr->clone();
   return 0;
}

Я создал методы i_clone и clone, чтобы каждый класс получал версию clone, которая возвращала указатель на собственный тип класса. Мне также нужно иметь объявление using в шаблонном классе B, чтобы убедиться, что нет никакой неоднозначности, в которой клон будет вызываться в производных классах.

Обратите внимание, что классы C и D не содержат повторяющегося кода, относящегося к созданию своих клонов.

В то время как a еще не знал об этом, это, кажется, еще одно изобретение Curiious Recurring Template идиома в применении к полиморфной конструкции копирования .

2 голосов
/ 13 января 2010

Немного сложно точно понять, что вы пытаетесь сделать (что такое SpBrd?), Но вы можете подумать о создании новой виртуальной функции, такой как:

class B
{
    ...
    virtual B *Clone() const { return new B(*this); }
    ...
};
class D: public B
{
    ...
    virtual D *Clone() const { return new D(*this); }
    ...
};

, который затем можно использовать так:

S.LEFT = this->Clone();
S.RIGHT = S.Clone();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...