Выбрать главу

void CIceberg::hit(CTitanic& _ct) {

 cout ‹‹ "ci+co" ‹‹ endl;

}

// Класс Титаника

class CTitanic {

public:

 virtual void hit(CIceberg&);

 virtual void hit(CTitanic&);

public:

 virtual void hit(CFloating&);

};

// Еще одна диспетчерская функция

void CTitanic::hit(CFloating& _co) { _co.hit(*this); }

// А вот эта функция могла бы быть реализацией

// но мы ее тоже делаем диспетчерской;

// в этом фрагменте диспетчеризация тройная.

void CTitanic::hit(CIceberg& _ci) {

 // cout ‹‹ "co+ci" ‹‹ endl; Это могла быть реализация

 _ci.hit(*this);

}

void CTitanic::hit(CTitanic& _ct) {

 cout ‹‹ "co+co" ‹‹ endl;

}

// проверим по быстрому, как работает

int main () {

 CIceberg i1;

 CTitanic t1;

 CIceberg i2;

 CTitanic t2;

 i1.hit(t1);

 i1.hit(i2);

 t1.hit(i1);

 t1.hit(t2);

 return 0;

}

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

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

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

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

Это - плата за отсутствие в C++ функций, виртуальных по отношению к 2 и более классам.

Шаг 15 - Как сделать массив из чего угодно.

Массивы и оператор operator[].

Давайте попробуем придумать класс, объекты которого вели бы себя как массивы? Поехали. Решим, что класс внутри себя должен иметь для простоты массив, ну там счетчик элементов… вроде больше нечему там быть. Ну раз так, то возьмем стек из Шага 13, для чистоты эксперимента выкинем спорные перегрузки operator+, operator+= и operator-, а для доступа к элементу пишем функцию int getat (int). Но что получается? Значит, добавление-изъятие мы пишем как функции только ради чистоты стиля, а других мотивов нет? А с доступом к элементу нам вообще ничего не мешает - пусть вместо getat() будет operator[](), а возвращает ссылку - ссылке же можно присвоить значение, а значит, работать будет в обе стороны, и на чтение и на запись!

class CArray {

private:

 int a[100];

 int iTop;

public:

// Тут смотреть нечего, конструкторы да присваивания, банально

 CArray ():iTop(0) {}

 CArray (const CArray& _ca) {

  iTop = _ca.iTop;

  for (int i=0; i++; i ‹100) a[i]= _ca.a[i];

 }

 CArray& operator=(const CArray& _ca) {

  if (this==&_ca) return *this;

  for (int i=0; i++; i ‹100) a[i]= _ca.a[i];

  iTop = _ca.iTop;

  return *this;

 }

 CArray& add (int _i) {a[iTop]=_i; iTop++; return *this;}

 int pop(void) {iTop-; return a[iTop+1];}

 // Две функции доступа к элементам массива

 int& getat (int _i){return a[_i];}

 int& operator[](int _i){return a[_i];}

};

// проверим наши рассуждения

CArray c;

int main() {

 c.add(1);

 c.add(2);

 c.add(3);

 c.add(4);

 c.add(5);

 c.getat(3) = 10;

 c[2]=20;

 return 1;

}

Разумеется, я пропустил ВСЕ детали, и важные и мелкие, но это не главное. Самое главное - последние две функции декларации.

Надеюсь, Вы понимаете значение сделанного? Вы снова Властелин. Allmighty God. А как же? Вы полностью контролируете все и всех. Как назвать того, кто издает законы, по которым живут все без исключения? Творения которого рождаются и умирают лишь по воле его? Нарушившего закон его постигает немедленная и неотвратимая кара? (Да-да, именно, как у Буча там: "сервер, не выполняющий… инварианты Господа нашего…" ой нет, не было такого, но он имел в виду!)