объявлен виртуальным
class a : virtual public base {}
class b : virtual public base {}
class c : public a, public b {}
a A1;
b B1;
c C1;
Объект обыкновенного базового класса располагается, как правило, в начале объекта производного класса и имеет фиксированное
смещение. Если же базовый класс является виртуальным, то требуется его динамическое размещение. Тогда в объекте производного
класса на соответствующем месте размещается не объект базового
класса, а ссылка на него, которая устанавливается конструктором.
Для вышеприведенного примера имеем
A1 B1 C1
--a------¬ --b-----¬ --c---------------¬
¦ ------¬ ¦ ------¬ ¦ --a-------¬ ¦
+--------+ ¦ +-------+ ¦ ¦ ¦ -------¬ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+ ¦ ¦
¦-base--¬<---- ¦-base-¬<---- ¦ ¦ ¦ ¦ ¦
¦L-------¦ ¦L------¦ ¦ L---------- ¦ ¦
L--------- L-------- ¦ --b-------¬ ¦ ¦
¦ ¦ ------¬¦ ¦
¦ +---------+ ¦¦ ¦
¦ ¦ ¦ ¦¦ ¦
¦ L---------- ¦¦ ¦
¦ -base---¬<---¦ ¦
¦ L--------<---- ¦
L-----------------
Таблицы виртуальных функций располагаются в каждом базовом
классе обычным образом.
Лекция 8. Пример использования виртуальных функций
-------------------------------------------------
Реляционная база данных (РБД) представляет собой таблицу,
состоящую из произвольного количества строк и столбцов. В качестве элементов, хранящихся в клетке такой таблицы могут быть целые,
вещественные числа, строки постоянной и переменной длины, значения даты и времени. Все типы элементов в одном столбце должны
совпадать. Количество строк и столбцов, названия столбцов и типы
элементов в них заранее не известны и определяются динамически,
то есть в процессе работы программы.
В нашем примере рассмотрим РБД, целиком размещенную в памяти.
8.1 Организация РБД в виде таблиц ссылок на объекты
--------------------------------------------------
Прежде всего, определим элементы, размещенные в клетках таблицы, как объекты соответствующих элементарных классов:
- class string - строки переменной длины;
- class integer - целые числа;
- class real - вещественные числа;
- class dat - дата;
- class time - время и т.д.
Перечень таких классов может быть расширен. Для каждого из
них необходимо определить множество функций и операций, который
будет использоваться при работе с таблицей: ввод и вывод значения
объекта, сравнение объектов, создание копии объекта, приведение к
базовым типам данных (int, long, char*), арифметических операций
над объектами и целыми и т.д.. Интерпретация последних должна
быть своя для каждого типа объектов. Каждый из этих классов сделаем производным от некоторого базового класса, роль которого будет рассмотрена позднее.
В качестве примера рассмотрим класс строк переменной длины.
class string : base
{
char *s; // Ссылка на строку
int sz; // Длина строки
public: int GET(); // Ввод строки
void PUT(); // Вывод строки
int CMP(base*); // Сравнение строк
char *NAME(); // Возвращает имя класса строк
base *COPY(); // Возвращает копию объекта
operator long(); // Преобразование к типу long // возвращает длину строки
operator char*(); // Преобразование к типу char* // возвращает копию строки
base& operator+(char*); // Операция "+ строка"
// присоединяет строку
string();
~string();
}
//------------------------------------------------------string::string() // Конструктор
{
s = NULL; // Строка пустая
sz = 0;
}
//------------------------------------------------------string::~string() // Деструктор
{
if (s !=NULL) delete s; // Освободить память
}
//------------------------------------------------------int string::GET()
{
char ss[80];
if (s !=NULL) delete s;
gets(ss); // Ввод строки и размещение ее
s = new char[sz = strlen(ss)+1];// в динамической памяти
strcpy(s,ss);
return(1); // Ввод всегда правильный
}
//------------------------------------------------------void string::PUT()
{ puts(s); }
//------------------------------------------------------int string::CMP(base* two) // Сравнение строк по алфавиту
{
string *p = (string*) two; // Преобразовать ссылку на
return(strcmp(s, p->s); // второй объект к классу строк
} // (переход от БК к ПК)
//-------------------------------------------------------char *string::NAME() // Возвращает имя класса строк
{ return("Строка"); }
//-------------------------------------------------------base *string::COPY() // Создание копии объекта
{ // без копирования значения
string *p = new string; //
return(p); //
}
//-------------------------------------------------------string::operator long() // Преобразование к типу long { // возвращает длину строки
return (sz);
}
//-------------------------------------------------------string::operator char*() // Преобразование к типу char* { // возвращает текстовое представchar *p = new char[sz]; // ление значения объекта
strcpy(p,s);
return(p);
}
//--------------------------------------------------------base& string::operator+(char* two) // Операция "+ строка"
{ // Конкатенация строки в объекте
char ss[80]; // и входной строки
strcpy(ss,s); //
strcat(ss,two);
delete s;
s = new char[sz = strlen(ss)+1];
strcpy(s,ss);
return(*(base*)this); // Возвратить неявную ссылку на объект
} // вложенного базового класса
//-------------------------------------------------------
Базовый класс "base" необходим исключительно для обеспечения
идентичного доступа к любому элементу базы данных независимо от
его класса. Это абстрактный класс, содержащий объявление всех вышеперечисленных функций и операций виртуальными.
class base
{
public:
virtual int GET()=0; // Ввод значения объекта
virtual void PUT()=0; // Вывод значения объекта
virtual int CMP(base*)=0; // Сравнение значений объектов
virtual char *NAME()=0; // Возвращает имя класса
virtual base *COPY()=0; // Возвращает копию объекта
virtual operator long()=0; // Преобразование к типу long
virtual operator char*()=0; // Преобразование к типу char*
virtual base& operator+(char*)=0;
// Операция "+ строка"
virtual ~base(); // Виртуальный деструктор для
// разрушения объекта ПК по
}; // ссылке на БК
Сама двумерная таблица объектов организована традиционным
для структур переменной размерности способом:
- элемент БД создается в динамической памяти при добавлении
строки к БД;
- строка БД представлена массивом ссылок на объекты класса
base. Сам массив также создается в динамической памяти при добавлении новой строки в БД;
- ссылки на строки собраны в массив, который создается
конструктором базы данных и заполняется при вызове функции добавления строки (таблица строк БД);
- объект класса БД (table) содержит ссылку TBL на таблицу
строк.
Особо следует остановиться на способе назначения столбцам
типов содержащихся в них элементов БД (или классов объектов). Это
делается при помощи строки заголовка БД - head. Этот массив содержит ссылки на объекты, классы которых идентифицируют типы элементов в соответствующих столбцах. При создании новой строки БД
виртуальной функцией COPY создаются копии объектов из строки заголовка БД, для которых затем вызывается виртуальная функция ввода значений GET.
Строка заголовка создается конструктором объекта класса БД.
Имеется меню типов элементов, которое представляет собой массив
ссылок (TYPE) на объекты классов string,integer,dat и т.д.. Экранное меню строится при помощи вызова виртуальной функции вывода
имени класса TYPE[i]->NAME(). После выбора строки меню ссылка на
соответствующий выбранный объект переносится в строку заголовка БД.
class table
{
int nc; // Количество столбцов
int nr; // Количество строк
char **names; // Имена стробцов
base **head; // Строка объектов заголовка БД
// для объявления типов объектов
base ***TBL; // Таблица строк БД
public:
void append(); // Добавление строки в БД
void sort(int); // Сортировка по значениям столбца
long averrage(int); // Подсчет среднего арифметического
// для столбца
base& operator()(int,int);
// Выбор объекта из БД
table(); // Конструктор - создание БД
~table(); // Деструктор - удаление БД
}
объект БД
TBL Массив строк БД
--¬ ---------¬0
¦------->+--------+.. Элемент БД
L-- +--------+i Строка БД string
base*** ¦ ----------->---------¬0 integer
+--------+ +--------+.. real
+--------+ +--------+j --dat-------¬
base** ¦ -------------->-base-----¬¦
+--------+ ¦L----------¦
base* ¦ ¦
L----------- base
head Строка заголовка БД
--¬ S0
¦-------------->---------¬0 -string---¬
L-- ¦ ------------------>-base---¬¦
base** +--------+ ---------->L--------¦
¦ --------------¬ L--------- +--------+ ¦ ¦ D0
¦ --------- ¦ -dat------¬
+--------+ L--->-base---¬¦
base* ¦L--------¦
L---------//------------------------------------------------------// Меню классов объектов (типов столбцов)
string S0;
dat D0;
time T0;
integer I0;
base *TYPE[] = {
(base*) &S0;
(base*) &D0;
(base*) &T0;
(base*) &I0;
};
//-----------------------------------------------------// Создание структуры БД
#define MAXCOL 30
#define MAXREC 1000
table::table()
{
int i,j,n;
char ss[80];
names = new char*[MAXCOL]; // Таблица адресов имен столбцов
head = new base*[MAXCOL]; // Таблица ссылок на объекты
for (nc=0; nc<MAXCOL; nc++) // заголовка БД
{ // Ввод имени столбца
gets(ss);
if (strlen(ss)=0) break;// Пустая строка - выход
name[nc] = new char[strlen(ss)+1];
//------ построение меню типов элементов БД
...
for (j=0; j<3; j++)
{
gotoxy(10,5+j);
cputs( TYPE[j]->NAME() );
}
//------ выбор типа столбца - n
head[nc] = TYPE[n]; // Ссылка на объект с классом,
// соответствующим классу
// объектов столбца
TBL = new base**[MAXREC];
nr = 0; // Таблица ссылок на строки БД
}
}
//------------------------------------------------------// Деструктор БД
tabe::~table()
{
int i,j;
for (i=0; i<nr; i++)
{
for (j=0; j<nc; j++)
delete TBL[i][j]; // Разрушение объекта i-ой строки
// j-го столбца по ссылке на
// объект БК
// (виртуальный деструктор)
delete TBL[i];
}
delete TBL;
for (j=0; j<nc; j++)
delete names[j];
}
//------------------------------------------------------// Добавление строки к БД
void table::append()
{
int i;
TBL[nr] = new base*[nc]; // Создание таблицы ссылок для строки БД
for (i=0; i<nc; i++) // Создание копий объектов ПК из строки
{ // заголовка БД
TBL[nr][i] = head[i]->COPY();
printf("Столбец %s типа %s :",names[i],head[i]->NAME());
// Вывод подсказки имени и типа столбца
while(TBL[nr][i]->GET() ==0);// Ввод значения нового объекта
}
nr++;
}
//-------------------------------------------------------// Нахождение среднего арифметического по заданному столбцу
long table::averrage(int n)
{
long r;
int i;
if (n<0 || n>=nc) return(0);
for (r=0, i=0; i<nr; i++) // Виртуальная операция преобра r += (long)(*TBL[i][n]);// зования класса base к long
return(r / nr);
}
//-------------------------------------------------------// Сортировка по заданному столбцу методом "пузырька"
void table::sort(int n)
{
int i,k;
base *p;
do
{
for (i=0; i< nr-1; i++) // Виртуальная функция сравнения