В этой статье будет рассказано:
- Использование классов
- Клиент-серверная модель
- Изменение реализации
- Обзор ситуации на текущий момент
Использование классов
В предыдущих статьях было показано, как определять класс и его методы Следующий шаг состоит в разработке программы, которая будет создавать и использовать объекты класса. Целью языка C++ является сделать применение классов насколько возможно простым — подобно базовым встроенным типам вроде int и char. Создавать объект класса можно за счет объявления переменной этого класса либо использования операция new для размещения в памяти объекта этого класса. Объекты можно передавать в аргументах, возвращать их из функций, присваивать один объект другому.
Язык C++ предоставляет средства для инициализации объектов, для обучения сіn и сout распознавать объекты и даже для выполнения автоматического приведения типов между объектами подобных классов. Пройдет некоторое время, прежде чем вы научитесь делать все эти вещи, но давайте начнем с наиболее простых свойств. Несомненно, вы уже видели, как объявлять объекты класса и вызывать функции-члены. В программе ниже приведен код программы, которая использует файлы интерфейса и реализации. В коде создается объект типа Stock по имени fluffy_the_cat.
Программа проста, тем не менее, она проверяет все средства, которые вы встроите в класс. Для компиляции полной программы применяйте приемы, предназначенные для многофайловых программ. В частности, компилируйте ее с файлом stock00.срр и обеспечьте наличие файла stock00.срр в том же каталоге или папке.
*знак решетки*include <iostream>
*знак решетки*include "stockOO.h"
int main()
{
Stock fluffy_the_cat;
fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
fluffy_the_cat.show();
fluffy_the_cat.buy(15, 18.125);
fluffy_the_cat.show();
fluffy_the_cat.sell(400, 20.00);
fluffy_the_cat.show() ;
fluffy_the_cat.buy(300000,40.125);
fluffy_the_cat.show ();
fluffy_the_cat.sell(300000,0.125);
fluffy_the_cat.show();
return 0;
}
Результат
Company: NanoSmart Shares: 20
Share Price: $12.5 Total Worth: $250
Company: NanoSmart Shares: 35
Share Price: $18.125 Total Worth: $634.375
You can't sell more than you have! Transaction is aborted.
Company: NanoSmart Shares: 35
Share Price: $18.125 Total Worth: $634.375
Company: NanoSmart Shares: 300035
Share Price: $40.125 Total Worth: $1.20389e+007
Company: NanoSmart Shares: 35
Share Price: $0.125 Total Worth: $4.375
Обратите внимание, что main () — это просто механизм для тестирования класса Stock. Когда класс Stock заработает должным образом, его можно будет применять в качестве пользовательского типа в других программах. Важнейшим моментом для использования нового типа является понимание того, что делают функции-члены; вы не должны задумываться о деталях реализации.
Клиент-серверная модель
Программисты, соблюдающие принципы ООП, часто обсуждают проект программ в терминах клиент-серверной модели. Согласно этой концепции, клиентом является программа, которая использует класс. Объявление класса, включая его методы, образует сервер, который является ресурсом, доступным нуждающейся в нем программе. Клиент взаимодействует с сервером только через открытый (public) интерфейс. Это означает, что единственной В ответственностью клиента и, как следствие — программиста, является знание интерфейса. Ответственностью сервера и, как следствие — его разработчика, является обеспечение того, чтобы его реализация надежно и точно соответствовала интерфейсу. Любые изменения, вносимые разработчиком сервера в класс, должны касаться деталей реализации, но не интерфейса. Это позволяет программистам разрабатывать клиент и сервер независимо друг от друга, без внесения в сервер таких изменений, которые нежелательным образом отобразятся на поведении клиента.
Изменение реализации
С выводом программы связан один момент, который может не устраивать — неподходящее форматирование чисел. Имеется возможность улучшить реализацию, не затрагивая интерфейс. Класс ostream содержит функции-члены, которые управляют форматированием. Не особо вдаваясь в детали, скажем, что с помощью метода setf() можно избавиться от экспоненциальной нотации
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
Этот вызов устанавливает флаг, который заставляет объект cout использовать нотацию с фиксированной точкой. Подобным же образом следующий оператор заставляет cout выводить три десятичных знака после точки:
std::cout.precision(3) ;
Эти средства можно использовать в методе show () для управления
форматированием, но следует учесть еще один момент. В случае изменения реализации метода внесенные модификации не должны влиять на другие части клиентской программы. Изменения в формате будут оставаться активными вплоть до следующих изменений, поэтому они могут повлиять на последующий вывод в клиентской программе. Следовательно, в show () должен быть предусмотрен возврат к состоянию
форматирования, которое было до вызова этого метода. Это можно сделать, с применением возвращаемых значений операторов установки формата:
std::streamsize prec =
std::cout.precision(3); // сохранение предыдущего значения точности
std::cout.precision(prec); // восстановление предыдущего значения
// Сохранение исходных флагов
std::ios_base::fmtflags orig = std::cout.setf(std::ios_base::fixed);
// Восстановление сохраненных значений
std::cout.setf(orig, std::ios_base::floatfield);
Во-первых, вспомните, что fmtf lags — это тип, определенный в классе iosbase, который находится в пространстве имен std, отсюда и такое довольно длинное имя типа для orig. Во-вторых, orig хранит все флаги, и оператор сброса использует эту информацию для восстановления установок в разделе float field, который включает флаги для
нотации с фиксированной точкой и экспоненциальной нотации. В-третьих, давайте не будем здесь сильно беспокоиться о деталях. Главный момент в том, что изменения ограничиваются файлом реализации и не влияют на программу, использующую этот класс. Итак, изменим определение метода в файле реализации следующим образом:
void Stock::show()
{
using std::cout;
using std::ios_base; ^
// Установка формата в #.###
ios_base::fmtflags orig =
cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision (3);
cout « "Company: " « company
« " Shares: " « shares « ' \n• ;
cout « " Share Price: $" « share_val;
// Установка формата в #.##
cout.precision (2);
cout « " Total Worth: $" « total_val « '\n';
// Восстановление исходного формата
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
После этой замены программу можно перекомпилировать. Теперь вывод будет выглядеть так:
Company: NanoSmart Shares: 20
Share Price: $12.500 Total Worth: $250.00
Company: NanoSmart Shares: 35
Share Price: $18.125 Total Worth: $634.38
You can't sell more than you have! Transaction is aborted.
Company: NanoSmart Shares: 35
Share Price: $18.125 Total Worth: $634.38
Company: NanoSmart Shares: 300035
Share Price: $40.125 Total Worth: $12038904.38
Company: NanoSmart Shares: 35
Share Price: $0.125 Total Worth: $4.38
Обзор ситуации на текущий момент
Первый шаг в проектировании класса заключается в предоставлении объявления класса. Объявление класса смоделировано на основе объявления структуры и может включать в себя данные-члены и функции-члены. Объявление имеет раздел private, и члены, объявленные в этом разделе, могут быть доступны только через функции-члены. Объявление также содержит раздел public, и объявленные в нем члены могут быть непосредственно доступны программе, использующей объекты класса. Как
правило, данные-члены попадают в закрытый раздел, а функции-члены — в открытый, поэтому типичное объявление класса имеет следующую форму:
class имяКласса
{
private:
объявления данных-членов
public:
прототипы функций-членов
};
Содержимое открытого раздела включает абстрактную часть проектного
решения — открытый интерфейс. Инкапсуляция данных в закрытом разделе защищает их целостность и называется сокрытием данных. Таким образом, использование классов — это способ, который предлагает C++ для облегчения реализации абстракций, сокрытия данных и инкапсуляции ООП.
Второй шаг в спецификации класса — это реализация функций-членов класса. Вместо прототипов в объявление можно включать полное определение функций, однако общепринятая практика состоит в том, чтобы определять функции отдельно, за исключением наиболее простых. В этом случае вам понадобится операция разрешения контекста для индикации того, к какому классу данная функция-член принадлежит.
Например, предположим, что класс Bozo имеет функцию-член Retort (), которая возвращает указатель на тип char. Заголовок функции должен выглядеть примерно так:
char * Bozo::Retort ()
Другими словами, Retort () — не только функция типа char *, это функция типа char *, принадлежащая классу Bozo. Полное, или уточненное, имя функции будет выглядеть как Bozo: :Retort(). Имя Retort (), с другой стороны, является сокращением уточненного имени, и оно должно использоваться только в определенных случаях, таких как в коде методов класса.
Другой способ описания этой ситуации — это говорить о том, что Retort имеет область видимости класса, поэтому необходима операция разрешения контекста для уточнения имени, когда оно встречается вне объявления и вне методов класса.
Для создания объекта, который является частным примером класса, применяется имя класса, как если бы оно было именем типа:
Bozo bozetta;
Это работает потому, что класс является типом, определенным пользователем. Функция-член класса, или метод, вызывается с использованием объекта класса. Это делается с помощью операции членства (точки):
cout << bozetta.Retort();
Код вызывает функцию-член Retort (), и всякий раз, когда код этой функции
обращается к определенным данным-членам, используются значения членов объекта bozetta.
Конец
Спасибо, если вы прочитали эту статью. Надеюсь вы что-то новое узнали для себя, и конечно же поняли. Подпишитесь, поставьте лайк, напишите комментарий, поддержите меня. Хотелось бы увидеть как улучшать статьи и чего не хватает. Буду анализировать и улучшать контент. Еще раз спасибо, до свидания! :)
#ооп #c++ #языкипрограммирования #классы #структура #компилятор #разработка #программирование #программа #интерфейс