Если базовый класс private, то в производном классе public и protected компоненты базового класса доступны для функций производного класса, но для следующего производного класса они будут считаться private, т.е. будут недоступны, private-компоненты базового класса недоступны в производных классах.
Конструктор производного класса должен вызывать конструктор своего базового класса:
class TBase
{ public: TBase( int s, int m, int d);
/* Другие компоненты класса TBase */
}
class TVect : public TBase
{ public: TVect ( int k, int s, int m int d): TBase(s, m, d)
{ /* инициализация остальных компонент TVect */};
Известно, что при объявлении массивов в Си/Си++ количество элементов массива задается константой и в дальнейшем не может быть изменено. При обращении к элементам массив отсутствует контроль выхода за пределы индексов массива, что приводит к трудно обнаруживамым ошибкам в программах. Построим систему классов для обработки динамических массивов, в которые можно добавлять новые элементы и исключить возможность выхода за пределы текущего размера массива. Общие свойства массивов с такими сойствами, не зависящие от типа элементов массива, объединим в классе TBase, а для массивов с различными типами элеменов образуем свои классы. Описания классов объединим в файле заголовков TBASEARR.H, а определения методов приведем в файле TBASEARR.CPP.
// файл TBASEARR.H
#include <string.h>
#include <iostream.h>
class TBase //базовый класс для массивов всех типов
{int size, //размер элемента
count, //текущее число элементов
maxCount, //размер выделенной памяти в байтах
delta; //приращение памяти в байтах
char *pmem; //указатель на выделенную память
int changeSize(); //перераспределение памяти
protected:
void* getAddr( ){return (void*) pmem;};
void addNewItem(void*); //добавление в конец массива
void error(const char* msg){cout <<msg<<endl;};
public:
int getCount() {return count;};
TBase(int s,int m,int d);
TBase();
TBase(TBase&);
~TBase();
};
/* Массив с элементами типа int */
class TIntArray: public TBase
{ public:
int getElem(int index); // Значение элемента по индексу
void putElem(int index,int &pe); // Замена значения элемента по индексу
void addElem(int& el); // Добавление элемента в конец массива
TIntArray& add(TIntArray&); // Сложение двух массивов поэлементно
TIntArray& subtract(TIntArray&); // Вычитание массивов
void printElem(int index); // Вывод значения элемента на экран
void print(); // Вывод на экран всего массива
TIntArray(int m,int d):TBase((int)sizeof(int),m,d){ }; /*Конструктор */
TIntArray(TBase& a):TBase( a ){}; /*Конструктор */
~TIntArray();
Определения методов приведены в файле TBASEARR.CPP:
#include <stdlib.h>
#include <constrea.h>
#include <tbasearr.h>
/* Методы класса TBase */
TBase::TBase(int s,int m,int d):size(s),maxCount(m),delta(d)
{char* p;
int k;
count = 0; p = pmem = new char [size * maxCount];
for (k=0; k < maxCount; k++)
{ *p = '\0'; p++;}
TBase::TBase():size(1),maxCount(10),delta(1)
count = 0; p = pmem = new char [size *maxCount];
TBase::TBase(TBase& b):size(b.size),maxCount(b.maxCount),delta(b.delta)
{ int k;
count = b.count; pmem = new char [size * maxCount];
for (k=0; k < maxCount * size; k++)
{ pmem[k] = b.pmem[k];}
TBase::~TBase ()
{ delete [ ] pmem; }
При описании объектных типов функции, имеющие сходное назначение в разных классах, могут иметь одинаковые имена, типы параметров и возвращаемого значения. При обращении к такой функции с указанием имени объекта компилятору известно, какая из одноименных функций требуется. В то же время к объектам производного типа можно обращаться по указателю на базовый тип и тогда на этапе компиляции нельзя установить, функция какого из производных типов должна быть вызвана. В ходе выполнения программы требуется проверять, на объект какого типа ссылается указатель и после такой проверки вызывать требуемую функцию. Эти действия называют “поздним” связыванием, в отличие от “раннего” связывания, при котором уже на этапах компиляции или редактирования связей можно установить адрес точки входа вызываемой функции. В объектно-ориентированных языках программирования для решения этой проблемы применяются виртуальные методы.
Функция-компонента класса объявляется как виртуальная указанием ключевого слова virtual. Функции-компоненты в производных классах, заменяющие виртуальную функцию базового класса должны объявляться с тем же именем, тем же списком параметров и типом возвращаемого значения, что и соответствующая функция базового класса. Если из производного класса не образуется новых производных классов, ключевое слово virtual в описании функции можно опустить.
Если в производном классе нет объявления функции с тем же именем, что и виртуальная функция базового класса, будет вызываться функция базового класса.
Виртуальная функция может быть объявлена в форме:
virtual void print ( ) = 0;
Такая функция называется “чистой” (pure) виртуальной функцией, а объектный тип, содержащий ее объявление, называется абстрактным объектным типом. В программе не могут создаваться экземпляры абстрактных типов, такой тип может использоваться только для образования производных типов, причем в производном типе следует либо снова определить эту виртуальную функцию как чистую, либо обявить ее как обычную виртуальную функцию, выполняющую конкретные действия.
Виртуальные функции особенно полезны, когда к методом класса требуется обращаться через указатель на экземпляр класса, а сам этот указатель имеет тип указателя на базовый класс. Пусть, например, в классе TBase объявлена чистая виртуальная функция print:
virtual void print ( ) = 0; // Чистая виртуальная функция
Тогда в производных классах должна быть объявлена замещающая ее функция print, выполняющая реальные действия:
class TIntArray : public TBase
{ /* Другие методы */
virtual void print ( );
class TRealArray : public TBase
В программе, использующей объекты классов TIntArray и TRealArray могут создаваться экземпляры этих классов с возможностью обращения к ним через указатель на базовый класс:
TBase *pb;
TIntArray aint(5,3);
TRealArray areal(4,2);
Тогда для печати массивов могут применяться операторы
pb = &aint; pb->print(); //Печать массива aint
pb = &areal; pb->print(); // Печать массива areal
Приведем еще один пример использования виртуальных функций. Пусть некоторый любитель домашних животных решил завести каталог своих любимцев и для каждого вида животных определил свой класс с общим базовым классом Pet. Для краткости ограничимся в описании каждого животного его кличкой и типовым излаваемым животным звуком с возможностью вывода на экран списка кличек и представления издаваемых ими звуков.
Программа:
struct Pet // Базовый класс
{ char *name;
virtual void speak() = 0;
Pet( char *nm){name=nm;}
struct Dog : public Pet
{ virtual void speak( ) { cout<<name<<“ говорит “”<<“ Ав - ав”<<endl; };
Dog(char *nm): Pet(nm) { };
struct Cat : public Pet
{ virtual void speak( ) { cout<<name<<“ говорит “
<<“ Мяу-Мяу”<<endl;
Cat(char *nm): Pet(nm) { };
int main ()
{ Pet *mypets[ ] = { new Dog(“Шарик”),
new Cat(“Мурка”),
new Dog(“Рыжий “)}; // Список животных
const int sz = sizeof( mypets)/ sizeof( mypets [ 0 ]);
for ( int k = 0: k < sz; k++)
mypets [ k ]->speak();
return 0;
Функция, объявленная в производном классе, может иметь доступ только к защищенным (protected) или общим (public) компонентам базового класса.
Функция, объявленная вне класса, может иметь доступ только к общим (public) компонентам класса и обращаться к ним по имени, уточненному именем объекта или указателя на объект.
Чтобы получить доступ к личным компонентам объектов некоторого класса Х в функции, не имеющей к ним доступа, эта функция должна быть объявлена дружественной в классе X:
class X
{ friend void Y:: fprv( int, char*);
/* Другие компоненты класса X */
Можно объявить все функции класса Y дружественными в классе X;
Страницы: 1, 2, 3, 4, 5, 6, 7