Я позволяю себе пропустить кучу деталей (например то, что вставка сто первого элемента слегка повесит программу), но обращаю внимание: определение конструктора копии и оператора присваивания - суровая необходимость. Не сделаете - пожалеете; почему - говорил в Шаге 5.
Еще нюанс: определение operator-- для выбора из стека есть, мягко говоря, спорный стиль; некоторые считают даже перегрузку operator‹‹ и operator›› для работы с потоками крайне неудачной идеей. Так что это я только для примера…
Пример имеет семантику значений, а не семантику указателей. Это в общем значит, что в коллекции хранятся примитивные значения, а не указатели или ссылки.
Кстати, запомните эти умные слова, их круто вставить в разговор с приятелями или (еще лучше) с начальством, чтобы запутать его в камом-нибудь вопросе или снять с себя ответственность за неверно принятые решения. Если особенно ответственность лежит так же и на начальстве (оно же и аналитик), то объяснение 'так и так, тут семантика значений' будет воспринято лучше, чем 'вы все - тупицы и недоучки, неспособные к проектированию и анализу'. Вообще, теоретические познания бывают как нельзя более кстати при поиске виновных, чем при повседневной работе. Вы можете колбасить программы на фокспре или хотя и на C++ c MTS и DCOM, но если Ваша программа повесит сервер бухгалтерии за день до зарплаты или годового отчета… то никакие познания не будут лишними, чтобы свалить вину на сисадминов!
Перед тем, как закончить Шаг, вернусь к стилю: если я для примера безграмотно переопределил операторы арифметики, это не значит, что то же самое должны делать Вы. Компилятор позволяет определить любое возвращаемое значение и запрограммировать любые действия, но понемногу изменяя семантику, в конце концов Вы выроете себе яму и попадете в нее. Есть еще одна опасность - поведение, приоритет и правила взаимодействия операторов ЗАДАНЫ раз и навсегда, а компилятор не может оценить Ваш интеллектуальный уровень, и действует так, как ДОЛЖЕН, а не так, как Вы ДУМАЕТЕ, что он должен. Из-за этого НИКОГДА не переопределяйте операторы operator&&() и operator||(), и всегда правильно задавайте возвращаемое значение операторов.
Шаг 14 - Двойная диспетчеризация. Продолжение.
В Шаге 4 мы говорили о двойной диспетчеризации. Она очень хорошо подходит при необходимости отображения одних объектов посредством других, но не только; она в общем применима, когда Вам нужно обрабатывать попарные (и более) взаимодействия объектов двух и более разных классов. Получается этакая табличка, на осях которой нарисованы классы, а в ячейках - функции их взаимодействия. Количество функций равно произведению столбцов и строк этой таблички. А если диспетчеризация тройная или выше? Тогда еще умножаем на количество слоев, и дальше и дальше…
Как бы упростить жизнь? А вот так - если взаимодействие двух объектов дает один результат, пусть этим и занимается одна функция. Попробуем перевести на человеческий язык:
Пусть есть класс CTitanic и класс CIceberg. Их карма в том, чтобы столкнуться. Четыре варианта взаимодействия: Столкновение двух Ctitanic не ведет ни к чему, если вообще возможно, двух CIceberg - у них там свои дела, столкновение CTitanic и CIceberg, как известно, к семи Оскарам, и столкновение CIceberg и CTitanic - к тому же самому. То есть функций всего три. Определим взаимодействие этих классов как функцию hit(). Вот код:
#include ‹iostream.h›
// Форвардные объявления
class CTitanic;
class CIceberg;
class CFloating;
// Абстрактный базовый класс
class CFloating {
public:
virtual void hit(CIceberg&)=0;
virtual void hit(CTitanic&)=0;
public:
virtual void hit(CFloating&)=0;
};
// Класс айсберга
class CIceberg {
public:
virtual void hit(CIceberg&);
virtual void hit(CTitanic&);
public:
virtual void hit(CFloating&);
};
// Первая диспетчерская функция
void CIceberg::hit(CFloating& _co) {
_co.hit(*this);
}
// Две реализации взаимодействия
void CIceberg::hit(CIceberg& _ci) {
cout ‹‹ 'ci+ci' ‹‹ endl;
}
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) {