Программирование >>  Оптимизация возвращаемого значения 

1 ... 4 5 6 [ 7 ] 8 9 10 ... 96


имеющей в качестве аргумента массив элементов BalancedBST. Но что бы ни случилось, вам это явно не понравится.

В другом обличье проблема возникает, если вы пытаетесь удалить массив элементов производного класса, используя указатели на базовый класс. Вот один из способов, который вы, не подозревая подвоха, могли бы испробовать:

/ / Удаляеммассив, предварительно сообщив об этом. voiddeleteArrayCostream&logStream, BST array [] ) {

logStream Удаляеммассив, расположенный по адресу static cast<void*>(array) \n; delete [ ] array;

BalancedBST *balTreeArray = Создаеммассивэлементов

newBalancedBST[50]; типаBalancedBST.

deleteArray (cout, balTreeArray) ; Сообщаем об удалении

массива.

Вы этого не замечаете, но здесь также происходит вычисление указателей. При удалении массива для каждого его элемента должен быть вызван деструктор (см. правило 8). Когда компилятор обрабатывает выражение

delete [ ] array;

ОН создает код, который делает приблизительно следующее:

/ / Удаляем элементы в массиве *аггау в порядке, обратном / / порядку их создания.

for (int i = количество элементов в массиве - 1; i >= О; - i) {

array[i].BST::~BST; Вызываетсядеструктор

} / / для элемента array [ i ] .

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

Заметьте, что вы вряд ли совершите ошибку по использованию полиморфизма в массивах, если ваш конкретный (не абстрактный) класс (такой как BalancedBST) не будет наследовать от другого конкретного класса (подобного BST). Как объяснено в правиле 33, разработка программ, в которых конкретные классы не наследуют один от другого, имеет много преимуществ. Советую вам перейти к правилу 33 и прочитать все об этих преимуществах.



Правило 4. Избегайте неоправданного использования конструкторов по умолчанию

Default constructor (конструктор по умолчанию) - это конструктор, который можно вызывать без аргументов. Конструкторы инициализируют объекты, а конструкторы по умолчанию инициализируют объекты, не используя никакую информацию из контекста, в котором создается объект. Иногда это очень разумно. Например, объекты, представляющие собой числа с известным основанием можно инициализировать нулем или специальным значением не определено . Объекты, выполняющие функции указателей (см. правило 28), можно инициализировать аналогичным образом. Структуры данных (связанные и индексированные списки, хэш-таблицы и т.п.) допускается создавать как пустые контейнеры.

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

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

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

class EquipmentPiece{ public:

EquipmentPiece(intIDNumber);

У класса EquipmentPiece нет конструктора по умолчанию, поэтому его использование в некоторых случаях вызывает сложности. Первый - это создание массивов.

Вообще говоря, не существует способов передать аргументы конструктору элементов массива, поэтому стандартным образом создать массив элементов EquipmentPiece невозможно:

EquipmentPiecebestPieces[10] ; Ошибка!Невозможно вызвать

конструктор EquipmentPiece.

EquipmentPiece *bestPieces =

newEquipment Piece [10] ; Ошибка! Та же проблема.

2 - 679



Есть три варианта, как преодолеть это ограничение. Для массивов, размещаемых статически, решение состоит в том, чтобы определить необходимые аргументы в момент создания массива:

int ID1, ID2 , ID3 , ...,ID10; Переменные для хранения

идентификаторов оборудования.

EquipmentPiecebestPieces[] = {

Нормально,аргументы EquipmentPiece(IDl) , передаются

EquipmentPiece(ID2) , в конструкторы.

EquipmentPiece (ЮЗ) ,

EquipmentPiece(ID10)

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

typedef EquipmentPiece* PEP; PEP - это указатель на

EquipmentPiece.

PEPbestPieces[10] ; Нормально,конструкторне

вызывается.

PEP*bestPieces = newPEP[10] ; Тоже нормально.

Затем нужно связать каждый указатель с соответствующим объектом

EquipmentPiece:

for {inti = 0; i<10; + + i)

bestPiece[i] = new EquipmentPiece (идентификационньй номер) ;

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

Нерационального расхода памяти можно избежать, если сначала выделить для размещения массива область неструктурированной памяти, а затем использовать буферизованный оператор new (см. правило 8) для создания в памяти объектов EquipmentPiece:

Выделяем неструктурированную область памяти, необходимую для размещения 10 объектов EquipmentPiece; более подробное / / описание функции operator new [ ] приведено в правиле 8 . void *rawMemory =

operatornew[] {10*sizeof(EquipmentPiece)); / / Настроимуказатель bestPieces на эту область, чтобы его / / можно было рассматривать как массив EquipmentPiece. EquipmentPiece *bestPieces =



1 ... 4 5 6 [ 7 ] 8 9 10 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика