Void Display(void)

Использование замещающих функций-членов

TD в памяти

 

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

Деструктор класса TBase освобождает память, на которую ссылается член basep. Для простоты деструктор объявлен встраиваемым:

~TBase() {delete basep; }

Аналогично, деструктор TDerived освобождает память, на которую ссылается член uppercasep. Опять же, объявление деструктора встраиваемым позволяет упростить код:

~TDerived() { delete uppercasep; }

В отличие от конструкторов, деструктор производного класса никогда не вызывает непосредственно деструктор базового класса.

 

С++ автоматически вызывает деструкторы, когда объекты класса выходят из области видимости.

Например при выходе объект класса president из области видимости С++ сначала вызывает деструктор производного класса, выполняющего следующий оператор:

delete up;

Затем С++ вызывает деструктора базового класса, выполняющий оператор:

delete basep;

Т.о., две строки удаляются а порядке, обратном порядку их создания.

 

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

@@@@@@@@@@

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

//ПРИМЕР

class TBase {

private:

char *sptr;

public:

TBase(const char *s)

{sptr = strdup(s); }

~TBase() {delete sptr; }

{ cout << sptr << ‘\n’; }

};

Объявленный класс похоже на те, что вы уже видели в этой главе. Конструктор TBase() вызывает strdup() для создания копии строкового аргумента в куче и присваивания адреса выделенного участка памяти закрытому члену sptr. Деструктор –TBase() освобождает эту память, когда объект TBase выходит из области видимости. Функция-член Display() отображает адресуемую строку с помощью встраиваемого оператора вывода в поток.

//бъявить объект типа TBase как

TBase state(“California”);

//затем вывести строку state оператором

state.Display();

Предположим, позже, при разработке программы, решили, что все подобные строки должны быть помечены строкой “state: ”. вы можете добиться этого с помощью следующих двух операторов:

cout << “state: ”;

state.Display();

Но придется сделать аналогичные модификации во всей программе. Лучшее решение – вывести новый класс из TBase и заместить наследованную функцию-член Display() модифицированной версией.

//можно написать производный класс //следующим образом:

class TState: public TBase {

public:

TState(const char *s): TBase(s ) {};

void Display(void);

// Замещающая функция

};

Вдобавок к конструктору, в производном классе объявлена новая функция-член Display(). Поскольку она идентична члену Display() базового класса, то замещает наследованную.

//реализация замещающей функции-//члена

void TState::Display(void)

{

cout << “state: ”;

// Новый оператор

TBase::Display();

/ Вызов замещенной функции-члена

}

 

Замещающая функция-член Display() выводит сначала “State: ”, затем вызывает Display() базового класса для совершения строки. Поставив TBase:: перед Display(), вы указали компилятору, что следует вызывать функцию-член из TBase.

 

//Объявление и использование объекта класса

TState state(“Ohio”);

State.Display();

//отображение строки

“State:Ohio”.

 

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

class TPennsylvania: public TState {

public:

TPennsylvania(): TState(“Penn

sylvania”) {}

};

Класс TPennsylvania выводится из TState. Новый конструктор, объявленный без параметров, передает символьную строку “ TPennsylvania ” конструктору базового класса. В следующих строках

TPennsylvania Pennsylvania;

Pennsylvania.Display();

создается объект с именем Pennsylvania и выводится название штата.

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

Можно организовать новый класс из TState:

class TPopulation: public TState {

private:

long population;

public:

TPennsylvania(long n, const char *name);

void Display(void);

};

Конструктор TPopulation имеет длинный целый параметр плотности населения и символьный указатель на название штата. Вы можете реализовать конструктор следующим образом:

TPopulation::TPopulation(long n,

const char *name)

private:

long population;

public: