абстрактный оператор +? - PullRequest
3 голосов
/ 21 июля 2009

У меня есть класс:

class base
{
public :
 base & operator +=(const int value) = 0;
 // base operator + (const int val) = 0; // HOW DO I DO THIS ?
};

И дочерний класс, производный от него

class derived : public base
{
public :
 derived() : m_val(0) {}
 derived(const derived & val) : m_val(val.m_val) {}
 base & operator = (const derived& value) // POSSIBLE TO RETURN A REFERENCE TO BASE
 { m_val = value.m_val; return *this; }
 base & operator +=(const int val)
 { m_val += val; return *this; }
 /* operator + overload here */
 // base operator + (...) // IMPOSSIBLE TO RETURN A VALUE
protected :
int m_val;
};

Мне действительно нужно, чтобы в базовом классе был определен оператор +, который в то же время должен оставаться абстрактным. Мне трудно достичь, так как типичный оператор + возвращает значение, в то время как абстрактный класс не может быть создан - так что я оказался в некоторой ситуации с уловкой № 22. Есть ли правильное решение для этого?

Привет

Ответы [ 5 ]

10 голосов
/ 21 июля 2009

Оператор + () почти всегда должен быть реализован как свободная функция, поэтому нет необходимости объявлять его виртуальным (что вы не делаете, но должны избегать синтаксической ошибки) в базе.

По сути, ваш дизайн неправильный - если вам нужно какое-то поведение типа оператора + (), которое не может обеспечить разумная реализация operator + (), используйте именованную функцию-член вместо оператора + ().

1 голос
/ 22 июля 2009

Я отвечаю на ваш вопрос здесь . (Когда можете, удалите этот «ответ» и отредактируйте свой ОП)

Вам следует взглянуть на std::advance, определенное в заголовке <iterator>.

int DoStuff(iterator &it)
{
    iterator a = it;
    std::advance(a, 2);

    // (...)
    return 0;
}

Если я неправильно понимаю ваш вопрос, простите: P

0 голосов
/ 22 июля 2009

У нас есть две конфликтующие проблемы:

  1. Вы хотите использовать абстрактный базовый тип в качестве типа передачи по значению.
  2. Вам действительно нужно абстрактное виртуальное поведение с чисто виртуальными функциями.

Одно решение: идиома "pimpl" или "letter-envelope". Основная идея: создать класс, который оборачивает указатель на класс реализации:

class impl {
public:
  virtual impl *do_add(const impl *other) const = 0;
};

class handle {
  impl *_impl;
  handle(impl *i): _impl(i) {}
public:
  handle(): _impl(NULL) {}

  handle operator+(const handle &other) const {
    impl *new_impl = _impl->do_add(other._impl);
    return handle(new_impl);
  }
};

Каждый виртуальный метод в классе impl имеет соответствующий не виртуальный метод в дескрипторе. Вы можете передавать маркеры по значению - все они имеют одинаковый размер и конструкцию, так как их «настройка» осуществляется с помощью указателей, на которые они указывают. И все базовые вызовы виртуальных функций выполняются через указатель, поэтому вы никогда не создаете экземпляр базового значения.

Затем вы пишете подклассы impl для каждой необходимой вам специализации.

Одна проблема: вы должны быть осторожны с управлением памятью. Вам нужно написать конструктор копирования и оператор присваивания для дескриптора. То, что они делают с импл, зависит от вас. Самое простое - сделать поверхностную копию указателя, но этого, скорее всего, недостаточно, поскольку вы не знаете, кто «владеет» указателем, то есть когда он может быть освобожден. Поэтому вам, возможно, придется либо всегда глубоко копировать impl (то есть _impl = other._impl-> clone (), где clone () - виртуальная функция, которая делает глубокую копию impl), либо использовать реализацию интеллектуального указателя. Если вы попробуете последнее, то убедитесь, что вы правильно настроены, и делайте глубокие копии для операций мутации.

0 голосов
/ 21 июля 2009

Привет всем, это ОП на альтернативном аккаунте.

НАЗНАЧЕНИЕ Написать универсальное семейство классов итераторов, которое будет способно перебирать std :: vectors и массивы T [], где T = byte = unsigned char.

ИДЕЯ ЗА АБСТРАКТНЫМ КЛАССОМ

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

** У меня нет компилятора со мной, поэтому, пожалуйста, прости меня за любые ошибки **

class iterator
{
  public :
    virtual iterator & operator -- ()    = 0;
    virtual iterator & operator -- (int) = 0;
    virtual iterator & operator ++ ()    = 0;
    virtual iterator & operator ++ (int) = 0;
    virtual iterator & operator += (int n) = 0;
    virtual iterator & operator -= (int n) = 0;
    virtual bool operator == (const iterator & o) = 0;
    virtual bool operator != (const iterator & o) = 0;
};

Итак, пример производного класса будет выглядеть так:

/*! \brief ptr iterator can be used to iterate over classic c-style arrays */
class ptr_iterator : public iterator
{
  public :
    ptr_iterator() : m_p(NULL) {}
    ptr_iterator(byte * b) : m_p(b) {}

    iterator & operator = (const ptr_iterator &o)
    { m_p = o.m_p; return *this; }
    iterator & operator = (byte * b)
    { m_p = b; return *this; }
    virtual iterator & operator -- ()
    { --m_p; return *this; }
    virtual iterator & operator -- (int)
    { m_p--; return *this; }
    virtual iterator & operator ++ ()
    { ++m_p; return *this; }
    virtual iterator & operator ++ ()
    { m_p++; return *this; }
    virtual iterator & operator += (int n)
    { m_p += n; return *this; }
    virtual iterator & operator -= (int n)
    { m_p -= n; return *this; }
    virtual bool operator == (const iterator & o)
    { return ((ptr_iterator*)&o)->m_p == m_p; }
    virtual bool operator != (const iterator & o)
    { return ((ptr_iterator*)&o)->m_p != m_p; }

  private :
    byte * m_p;
};

Помимо вышеупомянутого итератора, у нас может быть итератор, который будет перебирать std :: vector

шаблон класс std_iterator: публичный итератор { typedef имя типа C :: iterator c_iterator; общественность: iterator & operator = (const c_iterator & i) {m_it = я; вернуть * это; } iterator & operator = (const std_iterator & o) {m_it = o.m_it; вернуть * это; } * * Тысяча двадцать-один

virtual iterator & operator ++ ()
{ ++m_it; return *this; }
virtual iterator & operator ++ (int)
{ m_it++; return *this; }
virtual iterator & operator -- ()
{ --m_it; return *this; }
virtual iterator & operator -- (int)
{ m_it--; return *this; }
virtual iterator & operator += (int n)
{ m_it += n; return *this; }
virtual iterator & operator -= (int n)
{ m_it -= n; return *this; }

virtual bool operator == (const iterator &o)
{ return ((std_iterator*)&o)->m_it == m_it; }
virtual bool operator != (const iterator &o)
{ return ((std_iterator*)&o)->m_it != m_it; }
bool operator == (const c_iterator &i)
{ return m_it == i; }
bool operator != (const c_iterator &i)
{ return m_it != i; }

личное:

c_iterator m_it;

}; * 1 027 *

Хорошо, так что в основном вышеупомянутые итераторы способны выполнять итерации по массиву или вектору.

если бы мы использовали функцию со следующим интерфейсом:

int DoStuff(iterator &it); 

Все виды итераторов могли бы вызывать его - и это общая идея. Допустим, я имею дело с данными, поступающими в разных формах - от массивов до векторов:)

Дело в том, что мне нужны следующие функции:

std_iterator> it = my_vector.begin (); // ЭТО ОДИН РАБОТАЕТ std_iterator> other = it; // ЭТО ОДИН РАБОТАЕТ СЛИШКОМ

Если бы я был внутри вышеупомянутой функции

int DoStuff(iterator &it)
{
  iterator a = it + 2;   // THIS IS ILLEGAL DUE TO ABSTRACT-NESS OF THE BASE CLASS
                         // YET I STILL NEED THIS TO WORK. HOW DO I ACHIEVE THAT ?
                         // DO I SACRIFICE THE ABSTRACT-NESS OF THE BASE CLASS ?
                         // AT THIS POINT I WAS SERIOUSLY CONSIDERING AN OVERLOAD OF THE
                         // + OPERATOR BUT HAVE NO IDEA HOW TO DO IT WITH AN ABSTRACT 
                         // CLASS 
  // (...)
  return 0;
}
0 голосов
/ 21 июля 2009

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

Объясните, что вы пытаетесь сделать, и, вероятно, можно найти лучший способ.

...