valker's blog

valker's blog: идеи, размышления, наблюдения

Полиморфизм и все-все-все

with one comment

В этой заметке речь пойдёт о двух полюсах полиморфизма в С++.

Полюс первый

template <class Derived>
struct base {
  void interface(void) {
  // …
  }
};

Имея такой базовый класс, производный описывается так:
struct derived : base<derived>
{
void implementation(void);
};

Интересно? Да, но ничего необычного. Необычное начинается сейчас:
template <class Derived>
struct base {
  void interface(void) {
    // …
    static_cast<Derived*>(this)->implementation();
  }
};

Что мы здесь видим? Структура base параметризуется производным классом derived. Интерфейсная функция base::interface вызывает функцию реализации derived::implementation.
Это называется The Curiously Recurring Template Pattern (CRTP). По сути дела этот приём — аналог виртуальных функций на этапе компиляции. Плюсы такого подхода очевидны: отсутствие накладных расходов по вызову виртуальных функций, так как компилятор на этапе компиляции определяет, какую функцию следует вызвать и может эффективно соптимизировать такой вызов.
Минусы, на мой взгляд, следующие:

  1. Чтобы класс base мог вызвать derived::implementation() функцию, она должна быть объявлена с public доступом
  2. Объект не может изменить свой тип.

Этих недостатков лишены решения из второго полюса полиморфизма.

Полюс второй

На этом полюсе находится идиома конверт-письмо:
struct base {
  virtual void interface(void) {
    return letter_->interface();
  }
private:
  base* letter_;
};

На первый взгляд здесь всего только лишний указатель, лишний уровень косвенности. Но! Этот лишний уровень позволяет динамически менять тип объекта во время выполнения программы!!!

struct base {
  virtual void interface(void) {
    return letter_->interface();
  }
  explicit base(base* letter) : letter_(letter), isEnvelope_(letter != 0) {}
private:
  base* letter_;
  bool   isEnvelope_;
};

struct derivedFirst : base {
  derivedOne() : base(0) {}
  virtual void interface(void) {
    //… do something
  }
};

struct derivedSecond : base {
  derivedSecond() : base(0) {}
  virtual void interface(void) {
    //… do something
  }
}

base getFirst(void) {
  return base(new derivedFirst);
}

base getSecond(void) {
  return base(new derivedSecond);
}

Такая техника может быть применена, например, при проектировании библиотеки операций с числами. Базовый класс Number, классы Integer, Rational, Complex. Операции, изменяющие тип объекта:

Number sqrtP2 = squareRoot(Number(2));
Number sqrtN2 = squareRoot(Number(-2));

В первом случае тип объекта будет иррациональным, во втором — комплексным.
За подобную гибкость естественно приходится платить накладными расходами по вызову двух виртуальных функций.

ссылки по теме:

  1. http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
  2. http://en.wikipedia.org/wiki/C%2B%2B#Polymorphism

Удачи!

Written by valker

Июль 18, 2006 в 7:09 дп

Опубликовано в жизнь

Один ответ

Subscribe to comments with RSS.

  1. Hi!

    Evrything you have written is looking interesting, nice and useful.

    But it is not new.

    So, corresponding references, I believe, are necessary,
    especially to Lipman name, who invented both idioms:
    1. The Curiously Recurring Template Pattern (CRTP) and
    2. envelope-letter.

    I do not think that you tried to misappropriate the idioms, and I do believe that you know Lipman name and his authorship. But I am afraid that some readers, not that much experienced, can be mislead about the idioms author.

    I am sorry for writing English, but I am living now in such a place,
    where Cyrillic fonts are unknown and absent.

    Best regards, Yury.

    Yury

    Февраль 13, 2007 at 11:40 дп


Добавить комментарий

Please log in using one of these methods to post your comment:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: