Программирование >>  Структура ядра и системные вызовы 

1 ... 3 4 5 [ 6 ] 7 8 9 ... 98


-dates о {); <шилЦл

void set {int y) { year = y; }s

II функции-члены типа const - A;

void print( ostreams = cerr ) const;

int sameDay(int d) const ( return d==day; );

замечание: эта функция перегружена:

void set(int у) const { ::year = y; };

void dates::print{ ostreamS os ) const

OS year , month day Vn;

int main{)

const dates foo; . dates fool;

foo.set(1915); foo.printO; fool.set(25); fool.print 0; return 0;

const object

non-const object

::year = 1915

year=0, month=0, day=0

fool.year=25

year=25,month=0,day=0

В этом примере foo - const-объект, a fool - нет. Оба объекта инициализируются конструктором dates::dates. Оператор foo.set(1915) вызывает функцию-член dates::setTViTiu const, а оператор fool.set(25) - просто функцию-член dates::set. Оба оператора могут вызывать функцию-член dates::print. Это нормально, так как объекты, не относящиеся к типу const, всегда могут вызвать функции-члены типа const (но не наоборот).

Компиляция и пробный запуск этой программы дают такие результаты:

% СС const.с % a.out О, О, О 25, О, О

2.5. Наследование классов в С++

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

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

в следующем заголовке window.h объявляется класс window, который является подклассом класса menu:

II source file name: window.h #ifndef WINDOW H Idefine WINDOW H finclude <iostream.h> finclude menu.h

class window : public menu {

private:

int xcord, ycord; public:

конструктор

window! const int x, const int y, const char* str ) : menu(str) {

xcord = x; ycord = y;

деструктор ~window{) {);

функция, предназначенная для window

void show {ostreams os )

OS xcord , ycord => nameO endl;

fendif

в объявлении подкласса имя базового класса может предваряться ключевым словом public или private. Это означает, что открытые данные-члены и функции базового класса следует считать в подклассе соответственно открытыми или закрытыми. Если такого ключевого слова нет, по умолчанию они являются закрытыми.

Функция-член подкласса может непосредственно обращаться только к защищенным и открытым данным-членам своего базового класса (классов). Подкласс может явно отмечать определенные открытые или защищенные данные-члены и (или) функции закрытых базовых классов как соответственно открытые или защищенные в этом подклассе.

Например, класс window2 делает все данные-члены и функции класса menu, которые он наследует, закрытыми, за исключением переменной тепи::пит Jields, которая в этом подклассе трактуется как защищенная:

class window2:private menu

private:

int xcord, ycord; protected:

menu::num fields; сделать menu::num field защищенной

public:



Когда определяется объект подкласса, то функции-конструкторы этого подкласса и его базового класса (классов) вызываются в следующем порядке:

конструкторы базового класса в последовательности, указанной в объявлении подкласса;

конструкторы данных-членов в последовательности, объявленной в подклассе;

конструкторы подкласса.

Так в объявлении подкласса

class a,b;

class basel, base2;

class sub:public basel, private base2; {

a varl; b var2; public:

sub foo;

порядок вызова функций-конструкторов для переменной foo будет следующим: baselr.basel, base2::base2, а::а, b::b и, наконец, sub::sub.

Если объект подкласса находится вне области видимости, то функции-деструкторы этого подкласса и его базового класса (классов) вызываются в таком порядке:

деструктор подкласса;

Ij. деструкторы данных-членов в последовательности, обратной объявлен-fной в подклассе;

деструкторы базового класса в последовательности, обратной указанной в объявлении подкласса.

Если в предьщущем примере переменная foo выходит из области видимости, то порядок вызова функций-деструкторов будет следующим: sub::~sub, b::~b, а::~а, base2::~base2 и, наконец, basel::~basel.

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

<подкласс>::<подкласс> ( <список аргументов> ) : <список инициализации> /* тело */

<список инициализацииУ можно представить так: <имя класса> ( <аргумент> ) [, <имя класса> ( <аргумент> ) ]+ а функцию-конструктор класса sub можно записать следующим образом:

sub::sub( int x, int y, int z ) : basel(a), base2(b), a(z), b(z=l) {

/* тело функции */

Список инициализации приводится только в определении функции-конструктора подкласса, но не при ее объявлении. Более того, если конструктор класса не определен или определен, но не имеет аргументов, то имя этого базового класса или имя данного-члена в списке инициализации можно не указывать.

2.6. Виртуальные функции

Функция-член класса может объявляться ключевым словом virtual. Это означает, что все подклассы данного класса имеют право переопределять эту виртуальную функцию. Так, в С++ поддерживается полиморфизм - одна из основных особенностей объектно-ориентированного программирования. Подкласс может переопределять или не переопределять виртуальные функции, которые наследует от своих базовых классов. В первом случае он, однако, не имеет права изменять сигнатуру переопределяемых функций.

Виртуальные функции используются при определении общих операций для совокупности родственных классов. Интерфейс этих операций одинаков для всех классов, чего нельзя сказать о реальном поведении (и реализации) этих операций в каждом классе. Например, базовый класс menu может определить операцию draw, предназначенную для изображения на экране какой-то фигуры, а его подкласс window может переопределить эту операцию, которая после этого будет представлять на экране окно, а затем меню.

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

Пример использования виртуальных функций приведен ниже:

source module: virtual.С ♦include <iostream.h> class date {

int year, month, day; public:

. date (int y, int m, int d) { year=y; t4onth=m; day=d,; ); virtual ~date() {);

virtual void print() {cerr year 7 month /

day \n ;); virtual void set (int a, int b, int c){ year=a; month=b; day=c; };

class (derived : public date

int x; public:

derived (int a,int b,int c,int d): date(a,b,c), x(d)



-derived о (}; .j-ff

void print 0 { date: :print(); cout derived: x= x Хп!,- } ; virtual void set(int a, int b, int c) (x=a;}; \

int main 0

date foo(1997,5,4) ; derived yd,2,3, 4) ; date* p = Sy;

p->print(); derived::print()

p = & foo;

p->print(); date::print()

return 0;

В этом примере функции date::~date, date:-.print и date::set объявлены как виртуальные. Класс derived переопределяет виртуальные функции print и set. Когда в функции main переменная р указывает на у, оператор p->print() фактически вызывает функцию derived::print, а когда р указывает на foo, операторp->print() вызывает функцию date::print. Если бы функция date::print не бьша объявлена как виртуальная, то оба оператора в функции main вызывали бы только функцию date::print, а функция derived::рпппрактоваласъ бы как перегруженная функция функции date::print.

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

% СС virtual.Cxif % a.out i-v

1/2/3

derived: x=4 1997/5/4

2.7. Виртуальные базовые классы

Допустим, класс А и класс В порождены от класса Base. Если класс С произвести и от класса А, и от класса В, то у каждого объекта класса С будет по два экземпляра данных-членов класса Base, что, возможно, для какого-то приложения и нежелательно. Дабы гарантировать, что в такой ситуации (с множественным наследованием) каждому объекту класса С будет доступен только один экземпляр данных-членов класса Base, необходимо объявить класс Base как виртуальный и в объявлении класса А,тлъ объявлении класса В.

class Base

int х;

class А: virtual public Base

int y?;lf(

class В: virtual public Base int z;

class C: public A, private В int w;

Схема хранения объекта (например, foo) класса С будет выглядеть примерно так:

int у;

int z;

С int w;

Base

i ij int x;

. :.f.

Виртуальный базовый класс

Виртуальный базовый класс должен определить функцию-конструктор, которая либо не принимает аргументов, либо использует для всех своих аргументов значения по умолчанию. Виртуальный базовый класс инициализируется своим последним производным классом (например, класс Base инициализируется с помощью класса С, а не классом А или В). Если последний производный класс не инициализирует виртуальный базовый класс явно, то вызывается конструктор виртуального базового класса, который не требует аргументов и инициализирует объекты последнего производного класса. Конструкторы виртуальных базовых классов вызываются до конструкторов невиртуальных базовых классов, а их деструкторы - после деструкторов невиртуальных базовых классов.

Если в каком-то подклассе существуют открытый и закрьггый виртуальные базовые классы, то виртуальный базовый класс считается открытым в



1 ... 3 4 5 [ 6 ] 7 8 9 ... 98

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