Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / C / C++ / Введение в язык C++ / Производные Классы /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Производные Классы - Программирование от RIN.RU
Производные Классы



Конструкторы и Деструкторы


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


Например:


class base {
// ...
public:
base(char* n, short t);
~base();
};




class derived : public base {
base m;
public:
derived(char* n);
~derived();
};


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


Например:


derived::derived(char* n) : (n,10), m("member",123)
{
// ...
}


Объекты класса конструируются снизу вверх: сначала базовый, потом члены, а потом сам производный класс. Уничтожаются они в обратном порядке: сначала сам производный класс, потом члены а потом базовый.


Поля Типа


Чтобы использовать производные классы не просто как удобную сокращенную запись в описаниях, надо разрешить следующую проблему: Если задан указатель типа base*, какому производному типу в действительности принадлежит указываемый объект? Есть три основных способа решения этой проблемы:


  1. Обеспечить, чтобы всегда указывались только объекты одного типа (см. этот пункт);

  2. Поместить в базовый класс поле типа, которое смогут просматривать функции; и

  3. Использовать виртуальные функции (см. этот пункт ). Обыкновенно указатели на базовые классы используются при разработке контейнерных (или вмещающих) классов: множество, вектор, список и т.п. В этом случае решение 1 дает однородные списки, то есть списки объектов одного типа. Решения 2 и 3 можно использовать для построения неоднородных списков, то есть списков объектов (указателей на объекты) нескольких различных типов. Решение 3 - это специальный вариант решения 2, безопасный относительно типа.




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


enum empl_type { M, E };




struct employee {
empl_type type;
employee* next;
char* name;
short department;
// ...
};




struct manager : employee {
employee* group;
short level; // уровень
};


Имея это, мы можем теперь написать функцию, которая печатает информацию о каждом служащем:


void print_employee(employee* e)
{
switch (e->type) {
case E:
cout << e->name << "\t" << e->department << "\n";
// ...
break;
case M:
cout << e->name << "\t" << e->department << "\n";
// ...
manager* p = (manager*)e;
cout << " уровень " << p->level << "\n";
// ...
break;
}
}


и воспользоваться ею для того, чтобы напечатать список служащих:


void f()
{
for (; ll; ll=ll->next) print_employee(ll);
}


Это прекрасно работает, особенно в небольшой программе, написанной одним человеком, но имеет тот коренной недостаток, что неконтролируемым компилятором образом зависит от того, как программист работает с типами. В больших программах это обычно приводит к ошибкам двух видов. Первый - это невыполнение проверки поля типа, второй - когда не все случаи case помещаются в переключатель switch как в предыдущем примере. Оба избежать достаточно легко , когда программу сначала пишут на бумаге $, но при модификации нетривиальной программы, особенно написанной другим человеком, очень трудно избежать и того, и другого. Часто от этих сложностей становится труднее уберечься из-за того, что функции вроде print() часто бывают организованы так, чтобы пользоваться общность классов, с которыми они работают.


Например:


void print_employee(employee* e)
{
cout << e->name << "\t" << e->department << "\n";
// ...
if (e->type == M) {
manager* p = (manager*)e;
cout << " уровень " << p->level << "\n";
// ...
}
}


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


<<<  НазадВперед  >>>
 1  2  3  4  5  6 


 8  Комментарии к статье  8 8  Обсудить в чате

8  В тему

Введение

Альтернативные Интерфейсы

Добавление к Классу

Неоднородные Списки

Законченная Программа

Свободная Память

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь