Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекций ООПро.doc
Скачиваний:
37
Добавлен:
13.04.2015
Размер:
431.62 Кб
Скачать

34

Конспект лекций по курсу «Объектно-ориентированное программирование»

Литература:

  1. Б.Страуструп. Язык программирования C++, 3-е изд./Пер. с англ. – СПб.; М.: «Невский диалект» – «Издательство БИНОМ», 1999 г. – 991 с., ил.

  2. А. Пол. Объектно-ориентированное программирование на C++, 2-е изд./Пер. с англ. – СПб.; М.: «Невский диалект» – «Издательство БИНОМ», 1999 г. – 462 с., ил.

  3. C/C++. Программирование на языке высокого уровня/ Т.А. Павловская. – СПб.: Питер, 2001. – 464 с.: ил.

  4. Г. Буч. Объектно-ориентированный анализ и проектирование с примерами приложений на C++, 2-е изд./Пер. с англ. – М.: «Издательство «Бином», СПб: «Невский диалект», 1998 г. – 560 с., ил.

  5. Мейерс С. Эффективное использование C++. 50 рекомендаций по улучшению ваших программ и проектов: Пер. с англ. – М.: ДМК, 2000. – 240 с.: ил. (Серия «Для программистов»).

  6. Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования. – СПб: Питер, 2001. – 368 с.: ил. (Серия «Библиотека программиста»)

  7. По UML.

1. Введение

1.1. История развития C++.

(Язык Cбыл спроектирован Деннисом Ритчи в начале 70-х для созданияUNIX).

Создатель C++ – Бьерн Страуструп (BjarneStroustrup),AT&T.

1980 г. «Cс классами»

1998. Ратифицирован стандарт C++

Нововведение

Источник нововведения

Основные средства

C

Концепция классов (cпроизводными классами и виртуальными функциями)

Simula67

Средства перегрузки операторов и возможность помещения объявлений в любом месте, где может быть записана инструкция

Algol68

Шаблоны

Ada, параметризованные модули вClu

Механизм обработки исключений

Ada, Clu, ML

Множественное наследование, чисто виртуальные функции, пространства имен

Опыт использования C++

1.2. Использование C++

На C++ написаны операционные системы (полностью или частично).

Эффективностьпозволяет использоватьC++ для написания драйверов и других программ, предназначенных для работы в реальном времени.

Малый объемтребуемойпамяти.

Связь с ассемблером.

inc

++a

dec

--a

add

a+=

sub

a-=

mul

a*=

Как правило, ключевыми аспектами являются удобство сопровождения, расширяемость и простота тестирования. Эти свойстваC++ привели к его широкому использованию в областях, где совершенно необходима надежность, а также там, где требования к программам значительно меняются со временем (банковское дело, торговля, страхование, телекоммуникации, военные приложения).

На C++ решается множество численных, научных и инженерных задач. (Причина этого в том, что традиционные численные задачи должны совмещаться с графикой и вычислениями, связанными со структурами данных, которые не укладываются в традиционный Фортран.)

Графика и пользовательские интерфейсы – области интенсивного использования C++.

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

(Так, приложение, включающее в себя доступ к локальной и глобальной сетям, численные расчеты, графику, интерактивное взаимодействие с пользователем и обращение к базе данных, может быть написано целиком на С++. Традиционно эти области считались раздельными и обслуживались с использованием разных языков программирования.)

С++ может сосуществовать с фрагментами кода и программами, написанными на других языках.

C++ широко используется для обучения и исследований (моделирующие программы), т.к. он:

Реализация C++:

Для PC, Windows: Microsoft Visual C++ 6.0, 7.0, Borland C++.

Для UNIX-систем это – системный компиляторGNUv3.XX.

1.3. ООП

[Павловская]

В окончательном виде любая программа представляет собой набор инструкций процессора. Все, что написано на любом языке программирования, – более удобная, упрощенная запись этого набора инструкций, облегчающая написание, отладку и последующую модификацию программы. Чем выше уровень языка, тем в более простой форме записываются одни и те же действия. Например, для реализации цикла на ассемблере нужно записать несколько инструкций, позаботившись о размещении переменных в регистрах, а в Cили Паскале для этого достаточно одного оператора.

С ростом объема программы становится невозможным удерживать в памяти все детали, и становится необходимым структурировать информацию, выделять главное и отбрасывать несущественное. Этот процесс называется повышением степени абстракциипрограммы.

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

Следующий шаг – использование модулей. Модуль – это набор связанных процедур вместе с данными, которые они обрабатывают. При этом программа структурируется и информация группируется. Извне модуля информация видна в более естественном виде. Например, можно представлять в одном модуле все разнородные сведения, относящиеся к фигуре, вычерчиваемой на экране (положение, размер, ориентация (угол поворота), цвет).

Для работы с этими данными требуются специальные функции (напрмер переместить, повернуть, сменить цвет, прочертить). Они помещаются вместе с данными в модуль.

При этом для использования этих данных и функций не требуется полного знания того, как именно они написаны – необходимы только описания интерфейсов.

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

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

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

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

Класс является типом данных, определяемым пользователем. Тип задает внутреннее представление данных в памяти компьютера, множество значений, которое могут принимать величины этого типа, а также операции и функции, применяемые к этим величинам. Все это можно задать и в классе. Например, тип intопределяет, что величина этого типа (inti) занимает 4 байта, принимает значения от -231до 231-1 и ее можно складывать, вычитать, и т.д. Создаваемый тип данных (квадрат) определяет, что объект этого типа (конкретный квадрат) будет занимать столько байт, сколько необходимо для размещения отдельных данных (положение, размер, ориентация (угол поворота), цвет). Размещение и размер квадрата ограничены пределами экрана, угол поворота – от 0 до 90 градусов. Квадрат можно переместить, повернуть, сменить цвет, прочертить.

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

Объектно-ориентированное программирование (ООП) – основная методология программирования 90-х. Она берет начало в Simula 67 и продолжается в (ранних)Smalltalk,LISP,Cluи (более поздних)Actor,Eiffel, Objective C, Java и C++.

[Буч]: ООП – это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.

В данном определении можно выделить три высказывания:

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

  2. каждый объект является экземпляромкакого-либо определенногокласса;

  3. классы организованы иерархически.

[Пол]: Концепции ООП

  • Моделирование действий из реального мира

  • Наличие типов данных, определяемых пользователем

  • Сокрытие деталей реализации

  • Возможность многократного использования кода благодаря наследованию

  • Интерпретация вызовов функций на этапе выполнения

[Буч]: (Инкапсуляция – маскировка всех внутренних деталей, не влияющих на внешнее поведение.)

Инкапсуляция – это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение; инкапсуляция служит для того, чтобы изолировать контрактные обязательства абстракции от их реализации.

Inheritance – наследование.

Фазы трансляции

Программа на C++ состоит из:

  • одного или нескольких исходных файлов (с расширением .cpp);

  • нуля и более заголовочных файлов (с расширением .h).

Каждый этих файлов содержит некоторую часть текста программы.

Трансляция включает в себя три фазы (см. рис. 2):

а) фаза препроцессирования. Каждый исходный файл вместе со включенным в него (с помощью директивы #include) текстом из заголовочных файлов образует так называемуюединицу трансляции. Также обрабатываются другие директивы препроцессора;

б) фаза компиляции. Каждая единица трансляции преобразуется компилятором в объектный файл (с расширением .obj), который содержит машинный код и информацию для связывания с другими объектными файлами.

в) фаза компоновки. Объектные файлы объединяются в исполнимый модуль.

Рис.1. Трансляция программы на C++

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

Препроцессор

Препроцессор – текстовый процессор, который манипулирует текстом исходных файлов на первой фазе трансляции. Препроцессор позволяет: определять макросы, производить макроподстановку, производить условную компиляцию, включать указанные файлы, применять машинно-зависимые правила к определенным частям исходного кода.

Директивы препроцессора указывают действие, которое нужно выполнить препроцессору. Все директивы начинаются с символа #.

Директива макроопределения

#defineидентифзамещающий_текст

#defineидентиф(идентиф, идентиф, …, идентиф)замещающий_текст

используется для простейшей замены: во всех местах, где встречается лексема идентиф, вместо нее будет помещензамещающий_текст(от места макроопределения и до конца файла). Пример:

#define IDI_ICON1 101

#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))

#define CASE break; case

#define FOREVER for(;;)

Макросы имеют большое значение в C, но в C++ они используются значительно реже. Вместо макроподстановки, определяющей константу, рекомендуется определять константу с помощью ключевого слова const, а вместо макроподстановки функционального вида –inline-функции.

Директива #include.

#include "path-spec"

#include <path-spec>

Директивы включения могут быть вложенными (до 10 уровней).

#undefидентиф

#pragmaтокен-стринг #pragma vtordisp( off )

Область видимости (scope)

Каждое имя (не только имя переменной) в C++ может использоваться только в определенной области программы. Эта область называется областью видимости имени.

Существует 5 областей видимости: локальная о.в., о.в. функции, о.в. файла, о.в. класса, о.в. прототипа.

  1. Локальная область видимости. Имя, объявленное внутри блока, доступно только в этом блоке и в содержащихся в нем блоках, и только после точки объявления. Замещение имен.

  2. Область видимости функции. Такую о.в. имеют только метки для оператора goto. Метки могут использоваться в любом месте функции, но не за ее пределами.

  3. Область видимости файла. Любое имя, объявленное вне какого бы то ни было блока или класса, имеет о.в. файла. Имя доступно после точки объявления до конца файла. Доступ через унарный оператор ::i

  4. Область видимости класса. Имена членов класса имеют о.в. класса. Доступ через объект (. ->)и через бинарный операторclassname::i.

  5. Область видимости прототипа. Эту о.в. имеют имена, объявленные в прототипе функции.

Связывание (linkage)

Программа состоит из нескольких единиц трансляции, связываемых вместе. Способ, которым имена объектов и функций совместно используются в различных ед. трансляции, называется связыванием. Существует 3 типа связывания: внутреннее (staticinta;), внешнее (inta;externinta;) и отсутствие связывания (typedef,enum).

Виды переменных по типу выделяемой памяти (способы использования памяти)

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

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

  3. Свободная (динамическая) память, которую явно требует программа при размещении объектов, и которую она может освободить после того, как память больше не нужна (при помощи newиdelete).

Класс памяти (storage class)

Класс памяти управляет продолжительностью жизни и связыванием объектов (переменных) в C++.(auto, register, static иextern)

объекты

где существуют

Время жизни

создаются

уничтожаются

статические

в коде

глобальные – на этапе компиляции

локальные статические – на этапе выполнения в точке определения

после завершения программы

автоматические

в стеке

на этапе выполнения в точке определения

при выходе из блока

динамические

в свободной памяти

на этапе выполнения при вызове оператора new

при вызове оператора delete

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

Унарные операторы newиdeleteслужат для управления свободной памятью. Свободная память – это предоставляемая системой область памяти для объектов, время жизни которых напрямую управляется программистом. Программист создает объект с помощью ключевого словаnew, а уничтожает его, используяdelete. Это важно при работе с динамическими структурами данных, такими как списки и деревья. Еще один пример – создание объектов, которые используются после возвращения из функции, в которой они были созданы.

Оператор newпринимает следующие формы:

newимя_типа

newимя_типа инициализатор

newимя_типа [выражение]

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

int *p, *q;

p=new int(5);

q=new int[10];

delete выражение

delete [] выражение

Структуры

Массив – набор элементов одинакового типа.

Структура – набор элементов произвольных типов. Элемент структуры называется членом.

struct human{

char *name;

int age;

}; //точкасзапятойпосле'}'

void f(){

human vova;

vova.name="Vova";

vova.age=70;

human piter={"Петя",100};

human *pp=&vova;

pp->age+=1;

}

Имя структуры (human) называется теговым именем и является типом. Можно объявлять переменные типаhuman(vova,piter). Их можно присваивать, передавать в качестве аргументов и возвращать в качестве значения функции.

Инициализация структуры (см.прог.)

Для доступа к членам структуры используется оператор «точка» – «.» (vova.name). Конструкция вида

переменная_структуры.имя_члена

используется как простая переменная.

Оператор указателя структуры. К объектам типа структуры часто обращаются посредством указателей с помощью оператора «->» :

указатель_на_структуру->имя_члена

Это эквивалентно

(*указатель_на_структуру).имя_члена

Структура – тип данных, определяемый пользователем.

Определим тип стека.

const int max_len=1000;

struct stack{

int top;

char s[max_len];

};

void reset(stack *stk){stk->top=0;}

void push(stack *stk, char c){stk->s[stk->top++]=c;}

char pop(stack *stk){return stk->s[--stk->top];}

bool is_empty(const stack *stk){return stk->top==0;}

bool is_full(const stack *stk){return stk->top==max_len;}

void use_stack()

{

stack s;

char str[]="Using Stack!!!";

int i=0;

cout<<str<<endl;

reset(&s);

while(str[i]) push(&s,str[i++]);

while(!is_empty(&s))cout<<pop(&s);

cout<<endl;

// pop(&s);

}

Напечатается строка в прямом и обратном порядках.

КЛАССЫ

В C++ существуют два вида типов: встроенные и типы классов. Встроенные типы включают в себя:char,int,double. Типы классов включают в себя, например:string,vector,istream,CFile,CDialog.

Свои типы классов может создавать и программист. Если они хорошо написаны, то их также легко использовать, как и встроенные типы.

Класс применяется для определения типов, соответствующих концепциям приложения.

Функции-члены класса

Концепция structрасширена вC++ так, что функции могут быть членами структур. Объявление функции включается в объявление структуры и эта функция вызывается с использованием методов доступа, которые определены для членов структуры. Идея заключается в том, что функциональность, необходимая для работы с типом данных struct, должна быть прямо включена в объявление структуры.

Перепишем стек: (сначала определим функции в пределах struct)

struct stack2

{

enum{ max_len=100};

int top;

char s[max_len];

void reset();

void push(char c);

char pop();

bool is_empty()const;

bool is_full()const;

};

void stack2::reset() {top=0;}

void stack2::push(char c){s[top++]=c;}

char stack2::pop(){return s[--top];}

bool stack2::is_empty()const {return top==0;}

bool stack2::is_full()const {return top==max_len;}

Эти функции отличаются тем, что могут обращаться к именам членов класса (top,s) «как есть», непосредственно.

void use_stack2()

{

stack2 s;

char str[]="Using Stack!!!";

int i=0;

cout<<str<<endl;

s.reset();

while(str[i])

s.push(str[i++]);

while(!s.is_empty())

cout<<s.pop();

cout<<endl;

// pop(&s);

}

При вызове функций-членов применительно к конкретному объекту типа stack2 они действуют на указанные члены именно этого (своего) объекта:

stack2 data, operands;//создаются 2 отдельных объекта со своими

//top и s[]

//но функции не отдельные, а одни и те же

data.reset();//data.top=0;

operands.reset();//operands.top=0;

stack2 *pstack=&operands;

pstack->push('A');//operands.s[operands.top++]='A'

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

< Определение stack2, как написано выше >

Используется оператор разрешения области видимости “::” .

Управление доступом

Понятие структуры расширено в С++ так, что появилась возможность вводить закрытые (private) и открытые (public) члены. Это касается как членов данных, так и функций-членов структуры.

К открытым членам структуры имеется непосредственный доступ из любой функции, имеющей доступ к объекту структуры.

К закрытым членам имеют доступ не все функции, а только те, чьи полномочия включают право доступа к этим членам. Таковыми являются функции-члены структур. Другие категории, имеющие такое право, будут рассмотрены позднее.

struct primer{

public:

int a; //public member

double b(int); //public member

private:

int c; //private member

double d(int); //private member

};

double primer::b(int p)

{

a=7; //ok, can access all members

b(3); //ok

c=2; //ok

return d(1); //ok

}

double primer::d(int p)

{

a=7; //ok, can access all members

b(3); //ok

c=2; //ok

return d(1); //ok

}

double use(int p)

{

primer pr;

pr.a=7; //ok, public member

pr.b(3); //ok, public member

// pr.c=2; //error: cannot access private member

// return pr.d(1);//error: cannot access private member

return 1;

}

Переменная primer::cможет быть изменена с помощью вызова функцииprimer::b(), но не непосредственно.

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

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

Другое преимущество – локализация ошибок. Неправильное значение закрытого члена данных может быть вызвано только неверным кодом функций-членов.

Классыявляются формой структуры, у которой право доступа по умолчанию закрытое. Таким образом,structиclassвзаимозаменяемы при условии надлежащего определения прав доступа.

class a{

int b;//private

}

Конструкторы

Использование функций типа reset() илиinit() для инициализации объектов класса неэлегантно и подвержено ошибкам. Программист может забыть проинициализировать объект или сделать это дважды. Лучшим подходом будет предоставление программисту возможности объявить функцию, имеющую явное назначение – инициализация объектов. Ввиду того, что такая функция создает (конструирует) значения данного типа, она называетсяконструктором. Конструктор распознается по имени, которое совпадает с именем класса.

Конструкторы– это специальные функции-члены, которые определяют, как инициализируются объекты.

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

class stack3

{

public:

stack3(); //конструктор по умолчанию

stack3(char);//stack that contains 1 element

void push(char c);

char pop();

bool is_empty()const;

bool is_full()const;

private:

enum{ max_len=100};

int top;

char s[max_len];

};

Конструктор выполняется в момент создания объекта.

void use_stack3()

{

stack3 s; //выполняется конструктор по умолчанию

stack3 s1(’d’);//выполняется конструктор с одним аргументом

stack3 *ps=new stack3();//выполняется конструктор по умолч.

stack3 *ps1=new stack3(’d’);//выполн. конструктор c 1 арг.

delete ps;

delete ps1;

}

Определение конструктора

stack3::stack3():top(0){}

stack3::stack3(char c)//stack that contains 1 element

:top(1)

{

s[0]=c;

}

В определении конструктора используется новый для вас синтаксис. Между символом «:» и открывающей фигурной скобкой ({) находится ряд инициализаторов конструктора (constructorinitializers), в данном случае 1 инициализатор. Инициализаторы конструктора велят компилятору инициализировать заданные члены значениями, указанными в соответствующих круглых скобках. В частности,topустанавливается равным 0. Второй конструктор иниц.1 и выполняет присваивание.

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

  1. C++ - среда выделяет память для хранения объекта.

  2. C++ - среда инициализирует объект в соответствии со списком инициализации конструктора.

  3. C++ - среда выполняет тело конструктора.

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

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

Пример с 2 инициализаторами конструктора.

class complex{

complex(double real, double imag);

complex(double real);

};

complex::complex(double real, double imag)

{re=real; im=imag;}

complex::complex(double real)

{re=real; im=0;}

Использование:

complex c3(2,3); //инициализация

Конструктор не имеет возвращаемого значения (даже void).

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

Деструкторы

Некоторым классам требуется функция, которая гарантированно вызывается при уничтожении объекта (для закрытия файла, снятия блокировки и т.д.). Такие функции называются деструкторами. Имя деструктора – это имя конструктора с предшествующим символом «тильда»:

~stack3(){delete[] s};

Статические члены

Члены данных могут быть объявлены с использованием модификатора класса памяти static. Член данных, объявленный какstatic, разделяется всеми объектами своего класса и хранится в одном месте. Нестатические члены данных создаются для каждого экземпляра (т.е. объекта) класса. Если бы не наличие статических членов данных, сведения, необходимые всем экземплярам класса, должны были бы объявляться глобальными. Это разорвало бы отношения между данными и их классом. Статический член позволяет данным класса, которые не специфичны для отдельного экземпляра, существовать в области видимости класса.

Так как статический член данных не зависит от конкретного экземпляра, к нему можно обращаться без указания объекта:

имя_класса :: идентификатор

Здесь используется оператор разрешения области видимости.

Пример:

<сначала how_many_,

затем how_many(),

затем const max_len_>

//stat.h

classstack_stat

{

public:

stack_stat();

~stack_stat();

...

boolis_full()const;

staticinthow_many();

private:

staticconstintmax_len_=100;

inttop;

chars[max_len_];

staticinthow_many_;

};

//stat.cpp

#include"stat.h"

#include<cassert>

intstack_stat::how_many_=0;

stack_stat::stack_stat():top(0){++how_many_;}

stack_stat::~stack_stat(){--how_many_;}

...

boolstack_stat::is_full()const{returntop==max_len_;}

intstack_stat::how_many(){returnhow_many_;}

//use.cpp

#include"stat.h"

#include<iostream>

usingnamespacestd;

voiduse_stack_stat()

{

stack_stat *ps=newstack_stat();

stack_stat s;

// cout<<stack_stat::max_len()<<'\n';

cout<<stack_stat::how_many();

// ps->~stack_stat();

charstr[]="Using Stack!!!";

inti=0;

cout<<str<<endl;

while(str[i])

ps->push(str[i++]);

//*ps=reverse_order(*ps);

while(!ps->is_empty())

cout<<ps->pop();

cout<<endl;

// s.pop();

deleteps;

cout<<stack_stat::how_many();

}

В этом примере в переменной how_manyотслеживается, сколько объектов классаstack_statбыло создано.

Функции-члены типа static и const

C++ позволяет использовать функции-члены типаstaticиconst. Синтаксически статическая функция-член содержит модификаторstatic, предшествующий возвращаемому типу функции внутри объявления класса. Определение вне класса не должно включать этот модификатор:

<пример с stat1>

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

<Пример из stack3>

Обычная функция-член может и читать, и изменять члены данных объекта.

Константная функция-член может читать, но не может изменять члены данных объекта. То есть такая функция не изменяет состояние объекта.

Статической функции-члену недоступны члены данных объекта, но доступны статические члены класса.

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

class complex{

double re,im;

public:

double real()const{return re;}

double real_bad_version(){return re;}

}

void f(){

const complex c1(1,0);

complex cx(2,3);

cout<<c1.real()<<cx.real();//ok

cout<<c1.real_bad_version();//нескомпилируется

cout<<cx.real_bad_version();//ok

}