Модульное программирование. Пространства имен

Содержание

Слайд 2

Пространства имен Области действия и пространства имен Каждый программный объект имеет

Пространства имен

Области действия и пространства имен
Каждый программный объект имеет область действия

и время жизни, которые определяются видом и местом его определения. Существуют следующие разновидности областей действия:
- блок;
- прототип функции;
Слайд 3

Пространства имен - функция; - файл; - группа файлов, в пределе

Пространства имен

- функция;
- файл;
- группа файлов, в пределе включающая все файлы

программного проекта (глобальная область действия);
- класс;
- пространство имен (часть глобальной области).
Слайд 4

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

Пространства имен

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

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

Пространства имен Локальные объекты, объявленные в теле функции, действуют в пределах

Пространства имен

Локальные объекты, объявленные в теле функции, действуют в пределах конкретного

блока. Время жизни – время работы функции. В период работы функции эти объекты хранятся в программном стеке. От вызова к вызову их значения не сохраняются.
Если есть необходимость сохранить значение локальных объектов, их необходимо объявить с модификатором класса памяти static.
Слайд 6

Пространства имен В этом случае переменные будут храниться в программном сегменте

Пространства имен

В этом случае переменные будут храниться в программном сегменте и

время их жизни совпадает со временем работы программы.
Пример:
double func(double d)
{
static double temp = 3.5;
cout << " Temp: " << temp++ << endl;
return d*temp;
}
Переменная static double temp будет сохраняться от одного вызова функции к другому.
Слайд 7

Пространства имен Файл. Программный объект, определенный с описателем класса static вне

Пространства имен

Файл. Программный объект, определенный с описателем класса static вне любого

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

Пространства имен Но обратиться к нему можно через оператор разрешения области

Пространства имен

Но обратиться к нему можно через оператор разрешения области видимости

-::.
Класс. Компоненты класса (поля, методы), за исключением статических, имеют областью действия класс. Время жизни компонентов класса определяется промежутком времени от создания объекта до его разрушения.
Слайд 9

Пространства имен Пространства имен (именованные области). В С++ есть возможность явным

Пространства имен

Пространства имен (именованные области). В С++ есть возможность явным образом

задать область действия имен как часть глобальной области с помощью оператора namespace.
Слайд 10

Пространства имен Пространства имен Пространство имен (именованная область) служит для логического

Пространства имен

Пространства имен
Пространство имен (именованная область) служит для логического группирования определений,

объявлений и ограничения доступа к ним. Чем больше объем программы, тем актуальнее использование именованных областей. Их удобно использовать в больших программных проектах.
Слайд 11

Пространства имен Общий формат объявления именованной области следующий: namespace [имя_области] {

Пространства имен

Общий формат объявления именованной области следующий:
namespace [имя_области]
{ /* определения

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

Пространства имен Продолжение именованных областей можно делать в различных файлах. Рассмотрим

Пространства имен

Продолжение именованных областей можно делать в различных файлах.
Рассмотрим простой пример:
namespace

demo
{
int I = 1; // определение переменной
int k = 0; // определение переменной
// прототип функции
void fun_1(int);
// определение функции
int fun_2(int I, int j)
{
//
}
}
Слайд 13

Пространства имен Дальнейшее расширение пространства namespace demo { // int i

Пространства имен

Дальнейшее расширение пространства
namespace demo
{
// int i = 2; ошибка, повторное

объявление
void func_1(double);
}
Если имя области не задано (анонимная область), компилятор определяет его самостоятельно с помощью уникального идентификатора.
Слайд 14

Пространства имен Нельзя получить доступ из именованной области одного файла к

Пространства имен

Нельзя получить доступ из именованной области одного файла к неименованной

области другого файла.
В именованной области логичнее всего помещать объявления объектов, а их определения выносить в файлы реализации, например,
void demo:: func_1(double d)
{
// тело функции
}
Слайд 15

Пространства имен Такой прием обеспечивает разделение интерфейса и реализации. Объекты программы,

Пространства имен

Такой прием обеспечивает разделение интерфейса и реализации.
Объекты программы, определенные внутри

пространства имен, становятся доступными с момента объявления пространства. Обратиться к ним можно с помощью имени области и оператора доступа к области видимости, например, demo:: i == 100;
Слайд 16

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

Пространства имен

Если какое-либо имя из именованной области используется часто, его можно

сделать доступным с помощью оператора using, например, using demo:: I;.
После чего к нему можно обращаться без указания области видимости.
Слайд 17

Пространства имен Если требуется открыть всю область видимости, используется оператор using

Пространства имен

Если требуется открыть всю область видимости, используется оператор
using namespace

demo;
Вспомните, например,
using namespace std;
Именованные области могут быть вложены друг в друга.
Слайд 18

Преобразования в стиле языка С++ Преобразования в стиле С++ При выполнении

Преобразования в стиле языка С++

Преобразования в стиле С++
При выполнении программы производятся

явные и неявные преобразования величин из одного типа в другой. Неявные преобразования типов происходит в соответствие со стандартом языка, то есть, от «меньшего» типа к «большему». Например, величины типа bool, char будут автоматически приведены к типу int, а int и float – к типу double.
Слайд 19

Преобразования в стиле языка С++ Простой пример: int var_int =22; double

Преобразования в стиле языка С++

Простой пример:
int var_int =22;
double var_double = 3.45;
//
cout

<< var_int * var_double << cout;
То есть, выражение var_int * var_double, следует понимать как
(double)var_int * var_double;
Слайд 20

Преобразования в стиле языка С++ В действительности такие преобразования не делаются,

Преобразования в стиле языка С++

В действительности такие преобразования не делаются, поскольку

компилятор делает их автоматически.
Преобразование от «большего» типа к «меньшему» необходимо указывать явно, например,
var_int * (int)var_double;
Необходимо помнить, что в данном случае будет потеря точности.
Слайд 21

Преобразования в стиле языка С++ Общий формат преобразования в стиле С

Преобразования в стиле языка С++

Общий формат преобразования в стиле С следующий:
тип(выражение);
(тип)выражение;
Необходимость

в преобразовании типов возникает, например, в случае когда функция возвращает указатель на тип void, который необходимо присвоить переменной конкретного типа для последующих действий:
float *p = (float *)malloc(100 * sizeof(float));
Слайд 22

Преобразования в стиле языка С++ Еще один пример – это переопределение

Преобразования в стиле языка С++

Еще один пример – это переопределение операции

new, которая всегда возвращает указатель на тип void.
Явные преобразования типа являются источником возможных ошибок, поскольку вся ответственность за его результат возлагается на программиста. Поэтому в С++ введены операции, позволяющие выполнять частичный контроль над преобразованиями.
Слайд 23

Преобразования в стиле языка С++ Операция const_cast Эта операция служит для

Преобразования в стиле языка С++

Операция const_cast
Эта операция служит для удаления модификатора

const. Как правило она используется при передаче в функцию константного указателя на место формального параметра , не имеющего данного модификатора.
Слайд 24

Преобразования в стиле языка С++ Общий формат операции следующий: const_cast (выражение)

Преобразования в стиле языка С++

Общий формат операции следующий:
const_cast<тип>(выражение)
Обозначенный тип должен быть

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

Преобразования в стиле языка С++ Необходимость введения этой операции обусловлена тем,

Преобразования в стиле языка С++

Необходимость введения этой операции обусловлена тем, что

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

Преобразования в стиле языка С++ Рассмотрим пример: void func(int *arg) {

Преобразования в стиле языка С++

Рассмотрим пример:
void func(int *arg)
{
cout << " Arg:

" << *arg << endl;
}
//
const int var_int = 10;
func(&var_int); // Ошибка !!!
func(const_cast(&var_int)); // Ошибки нет
Слайд 27

Преобразования в стиле языка С++ Еще один пример, более близкий к

Преобразования в стиле языка С++

Еще один пример, более близкий к ООП:
class

Test
{
int test;
public:
Test(){};
Test(int t):test(t){};
void Out()
{ cout << " Test: " << test << endl; }
};
Слайд 28

Преобразования в стиле языка С++ Объявим константный указатель на объект данного

Преобразования в стиле языка С++

Объявим константный указатель на объект данного класса:
const

Test *ptr_Test = new Test(100);
Попытка следующего обращения приведет к ошибке
ptr_Test->Out();, но
вызов
const_cast(ptr_Test)->Out();
будет абсолютно верным.
Слайд 29

Преобразования в стиле языка С++ Ошибку подобного рода можно избежать, объявив

Преобразования в стиле языка С++

Ошибку подобного рода можно избежать, объявив метод

Out() как константный (безопасный):
void Out() const
{
cout << " Test: " << test << endl;
}
Слайд 30

Преобразования в стиле языка С++ Операция static_cast Операция static_cast используется на

Преобразования в стиле языка С++

Операция static_cast
Операция static_cast используется на этапе компиляции

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

Преобразования в стиле языка С++ Формат операции: static_cast (выражение) Результат операции

Преобразования в стиле языка С++

Формат операции:
static_cast<тип>(выражение)
Результат операции имеет указанный тип, который

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

Преобразования в стиле языка С++ Например, float f = 100; int

Преобразования в стиле языка С++

Например,
float f = 100;
int i = static_cast(f);
Преобразования

подобного рода должны иметь серьезное основание. Результат преобразования остается на совести программиста.
Перечисленные преобразования попробуйте самостоятельно.
Слайд 33

Преобразования в стиле языка С++ Мы же рассмотрим пример преобразований в

Преобразования в стиле языка С++

Мы же рассмотрим пример преобразований в иерархии

родственных классов, что для нас имеет больший интерес.
Рассмотрим пример простой иерархии.
Слайд 34

Преобразования в стиле языка С++ Базовый класс: class Base { protected:

Преобразования в стиле языка С++

Базовый класс:
class Base
{
protected:
int base;
public:
Base(){};
void Out();
};
void Base::Out()
{
cout <<

" Base class " << endl;
}
Слайд 35

Преобразования в стиле языка С++ Производный класс: class Derived : public

Преобразования в стиле языка С++

Производный класс:
class Derived : public Base
{
int derived;
public:
Derived():Base(){};
void

Out();
};
void Derived::Out()
{
cout << " Derived class " << endl;
}
Слайд 36

Преобразования в стиле языка С++ Рассмотрим несколько примеров преобразований между этими

Преобразования в стиле языка С++

Рассмотрим несколько примеров преобразований между этими классами.
Преобразования

«вверх», то есть от объекта производного типа к типу базового класса, относятся к стандартным преобразованиям и не требуют явных преобразований. Например,
Derived der;
Base base = der;
Слайд 37

Преобразования в стиле языка С++ Выражение Base base = der; вполне

Преобразования в стиле языка С++

Выражение Base base = der; вполне оправдано,

хотя правильнее (понятней) была бы запись: Base base = (Base)der;,
или Base base = static_cast(der);.
Как было сказано, что преобразования «вверх» относятся к стандартным, то последнее преобразование не является обязательным.
Слайд 38

Преобразования в стиле языка С++ Рассмотренное преобразование типа производного класса возможно,

Преобразования в стиле языка С++

Рассмотренное преобразование типа производного класса возможно, если

производный класс описан с обобществленным базовым классом, как в нашем примере:
class Derived : public Base {}
Если объявить базовый класс как защищенный или закрытый
class Derived : protected Base {}
Подобные преобразования будут возможны, но не доступны.
Слайд 39

Преобразования в стиле языка С++ Преобразования объектов производного класса к типу

Преобразования в стиле языка С++

Преобразования объектов производного класса к типу базового

встречается на практике достаточно часто, например при инициализации объекта базового класса объектом производного.
Более интересный пример – попытка преобразования «вниз», то есть из типа базового класса в производный тип.
Слайд 40

Преобразования в стиле языка С++ Следует оговориться, что преобразования допустимы только

Преобразования в стиле языка С++

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

указателей или ссылок, но не объектов.
Для рассмотренных классов:
Base *ptr_Base = new Base();
ptr_Derived = static_cast(ptr_Base);
ptr_Derived->Out();
Что можно ожидать в этом случае?
Слайд 41

Преобразования в стиле языка С++ Сработает функция производного класса и выведет

Преобразования в стиле языка С++

Сработает функция производного класса и выведет «Derived

class».
А что будет при вызове ptr_Base->Out(); ?
В данном случае вывод будет: «Base class».
Ничего странного, ничего не обычного.
Изменим несколько объявления:
Base *ptr_Base = new Derived();
Слайд 42

Преобразования в стиле языка С++ Такое объявление также допустимо, указатель на

Преобразования в стиле языка С++

Такое объявление также допустимо, указатель на базовый

класс инициализируется значением указателя на производный.
Вызов ptr_Base->Out(); приведет к активизации функции базового класса.
Для того чтобы активизировать функцию производного класса, функцию базового класса нужно описать как виртуальную: virtual void Out();
Это проявление полиморфизма в С++.
Слайд 43

Преобразования в стиле языка С++ Операция dynamic_cast Эта операция используется в

Преобразования в стиле языка С++

Операция dynamic_cast
Эта операция используется в основном для

преобразования указателей и ссылок на объекты базового типа в указатели и ссылки на производный тип. При этом во время выполнения программы появляется возможность проверки (контроля) допустимости преобразований.
Слайд 44

Преобразования в стиле языка С++ Несложно догадаться, что преобразования будут выполняться

Преобразования в стиле языка С++

Несложно догадаться, что преобразования будут выполняться в

период выполнения программы.
Общий формат операции:
dynamic_cast<тип>(выражение)
Выражение должно быть указателем или ссылкой на класс, тип – базовым или производным для данного класса.
Слайд 45

Преобразования в стиле языка С++ В случае успешного выполнения операции формируется

Преобразования в стиле языка С++

В случае успешного выполнения операции формируется результат

заданного типа, в противном случае для указателей результат равен нулю, а для ссылок порождается исключение bad_cast. Если заданный тип не относится к одной иерархии, преобразования не допускаются.
Все дальнейшие рассуждения при рассмотрении наследования и полиморфизма.
Слайд 46

Преобразования в стиле языка С++ Операция reinterpret_cast Операция reinterpret_cast применяется для

Преобразования в стиле языка С++

Операция reinterpret_cast
Операция reinterpret_cast применяется для преобразования не

связанных между собой типов, например, указателе в целые типы или наоборот, а также указателей типа void в конкретный тип. При этом внутреннее представление данных не меняется, меняется только точка зрения компилятора на данные.
Слайд 47

Преобразования в стиле языка С++ Рассмотрим пример: struct Struct { int

Преобразования в стиле языка С++

Рассмотрим пример:
struct Struct
{
int str_int;
string str_string;
Struct(int s_i, string

s_s):
str_int(s_i), str_string(s_s){};
void Out()
{ cout << str_int << ' ' << str_string << endl; }
};
Слайд 48

Преобразования в стиле языка С++ Далее использование объекта этого типа через

Преобразования в стиле языка С++

Далее использование объекта этого типа через указатель

на тип void *
Struct str(120, "string");
void *ptr_void = &str;
reinterpret_cast(ptr_void)->Out();
Этот пример того, что не следует делать в практическом программировании, поскольку результат операции останется на совести программиста.
Слайд 49

Преобразования в стиле языка С++ Практическое использование этого оператора при форматированном

Преобразования в стиле языка С++

Практическое использование этого оператора при форматированном вводе-выводе

числовых величин.
#include
#include
const int MAX = 100;
int buffer[MAX];
Слайд 50

Преобразования в стиле языка С++ // создаем выходной поток ofstream os(“data.dat”,

Преобразования в стиле языка С++

// создаем выходной поток
ofstream os(“data.dat”, ios::binary);
// записываем

в него
os.write(reinterpret_cast(buffer),
Max*sizeof(int));
// закрываем поток
os.close();