Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 28

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 22 23 24 25 26 27 < 28 > 29 30 31 32 33 34 .. 105 >> Следующая

template<class Т> • . Jj
class Array {
public:
Array(int lowBound, int highBound);
private: ,
vector<T> data; Il Данные массива хранятся в векторе;
// о шаблоне vector см. правило 49. size_t size; Il Число элементов массива,
int lBound, hBound; Il Нижняя и верхняя границы.
};
template<class Т>
Array<T>:: Array (int lowBound, int highBound) : size (highBound - lowBound + 1), lBound(lowBound), hBound(highBound), data(size)
{}
Любой конструктор программного продукта промышленного уровня выполнял бы проверку допустимости параметров, чтобы убедиться, что highBound по крайней мере не меньше lowBound, но здесь кроется и гораздо более неприятная ошибка: даже при идеальных граничных значениях никак нельзя сказать, сколько элементов содержит data.
«Как это может быть? - слышу я. - Мы аккуратно инициализировали size, прежде чем передавать его конструктору vector!» К сожалению, вы заблуждаетесь: вы лишь пытались это сделать. Здесь действует следующее правило: члены списка инициализации класса инициализируются в том же порядке, в котором они объявляются в классе; порядок их следования в списке инициализации не имеет ни малейшего значения. В классах, генерируемых из нашего шаблона Array, вначале будет инициализирован data, затем size, lBound, и, наконец, hBound. Так будет происходить всегда.
Хотя это может показаться дикостью, для упомянутого правила есть причина. Рассмотрите следующий сценарий:
class Wacko { public:
Wacko(const char *s) : sl(s), s2(0) {} Wacko (const Wacko& rhs) .- s2(rhs.sl), sl(0) {} private:
string si, s2 ;
>;
Wacko wl = "Hello world! " ; Wacko w2 = wl;
Правило 13
Если бы члены класса инициализировались в порядке их следования в списке инициализации, порядок создания элементов данных wl и w2 был бы различным. ?crioMHHTe, что деструкторы элементов данных объекта всегда вызываются в порядке, обратном порядку вызова их конструкторов. Таким образом, если бы порядок инициализации определялся порядком следования в списке инициализации, для того чтобы гарантировать, что деструкторы будут вызваны в правильной последовательности, компилятору необходимо было бы отслеживать порядок инициализации каждого объекта. Занятие, согласитесь, весьма дорогостоящее... Дабы избежать лишних затрат ресурсов, порядок создания и уничтожения для объектов данного типа всегда один и тот же, а порядок в списке инициализации игнорируется.
Если уж вдаваться в детали, следует оговориться, что согласно этому правилу инициализируются только нестатические члены класса. Статические члены класса подобны глобальных! объектам и объектам, определенным в пространстве имен, и поэтому инициализируются только один раз; подробнее см. правило 47. Более того, члены базовых классов инициализируются прежде членов производных классов, поэтому если вы используете наследование, то должны инициализировать члены базовых классов в самом начале списка. (Если используется множественное наследование, созданные вами базовые классы будут инициализированы в порядке наследования; порядок, в котором они перечисляются в списке инициализации, снова окажется проигнорирован. Однако при использовании множественного наследования у вас появятся более серьезные причины для беспокойства. Правило 43 подскажет вам, какие аспекты множественного наследования заслуживают пристального внимания.)
Подытожим все вышесказанное: если вы действительно хотите понимать, что происходит при инициализации объектов, не забывайте вносить их в список ини-изации в том порядке, в котором они объявляются в классе.
Правило 14. Убедитесь, что базовые классы имеют виртуальные деструкторы
Иногда бывает удобно отслеживать, сколько всего объектов данного класса существует в вашей программе. Наиболее простой способ достичь этого - создать статический член класса для подсчета объектов. Этот член инициализируется нулем, инкремеитируется в конструкторах класса и декрементируется в деструкторах.
Вы можете представить себе некоторое военное приложение, в котором класс, представляющий собой вражеские цели, мог бы выглядеть приблизительно следующим образом:
class EnemyTarget { public:
EnemyTarget() { ++numTargets; }
EnemyTarget(const EnemyTarget&) { ++numTargets; } -EnemyTarget() { --numTargets; } static size_t numberOfTargets () { return numTargets; }
virtual bool destroy () ; Il Удачна ли была попытка уничтожить
// объект EnemyTarget?
¦^H ИННЯІІІ
Конструкторы и операторы
private:
static size_t numTargets; // Счетчик объектов.
};
Il Статистические данные должны быть определены вне класса; // переменная инициализируется 0 по умолчанию. size_t EnemyTarget::numTargets;
Этот класс вряд ли поможет вам подписать контракт с министерством обороны, но он вполне подойдет для наших целей. По крайней мере, я на это надеюсь.
Давайте предположим, что один из типов вражеских целей - это танк, который вполне логично моделировать (см. правило 35) классом, открыто наследующим от EnemyTarget. Поскольку наряду с суммарным количеством вражеских целей для вас представляет интерес суммарное количество танков, вы проделываете для него то же самое, что и для базового класса:
Предыдущая << 1 .. 22 23 24 25 26 27 < 28 > 29 30 31 32 33 34 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100