Инициализация статических член-данных
Пример.
// Пример встраиваемой функции #include <iostreara> using namespace std; inline int even (int x) { return ! (x%2); int main () { if (even (10)) cout ≪ "10 является четным\п"; if (even (11)) cout ≪ "11 является четным\п"; return 0; } </iostreara> |
В примере функция even(), которая возвращает истину при четном аргументе, объявлена встраиваемой. Это означает, что строка
if (even(10)) cout ≪ "10 является четным\n"; |
функционально идентична строке
if (! (10%2)) cout ≪ "10 является четным\n"; |
Важно. Особенность использования встраиваемой функции: она должна быть задана до ее первого вызова. Если это не так, компилятор не будет знать, какой именно код предполагается встроить в программу с помощью встраиваемой функции. Поэтому функция even() была определена перед функцией main().
Билет 13:
Вопрос 1:Типы данных языка С. Примеры.
- char - символьный;
- int - целый;
- float - вещественный;
- double - вещественный двойной точности;
- void - не имеющий значения.
Тип данных определяет множество значений, набор операций, которые можно применять к таким значениям и способ реализации хранения значений и выполнения операций.
Процесс проверки и накладывания ограничений на типы используемых данных называется контролем типов или типизацией программных данных. Различают следующие виды типизации:
· Статическая типизация — контроль типов осуществляется при компиляции.
· Динамическая типизация — контроль типов осуществляется во время выполнения.
Язык Си поддерживает статическую типизацию, и типы всех используемых в программе данных должны быть указаны перед ее компиляцией.
Различают простые, составные и прочие типы данных.
Простые данные
Простые данные можно разделить на
· целочисленные,
· вещественные,
· символьные
· логические.
Составные (сложные) данные
· Массив - индексированный набор элементов одного типа.
· Строковый тип - массив, хранящий строку символов.
· Структура - набор различных элементов (полей записи), хранимый как единое целое и предусматривающий доступ к отдельным полям структуры.
Другие типы данных
· Указатель - хранит адрес в памяти компьютера, указывающий на какую-либо информацию, как правило — указатель на переменную.
Программа, написанная на языке Си, оперирует с данными различных типов. Все данные имеют имя и тип. Обращение к данным в программе осуществляется по их именам (идентификаторам).
Идентификатор — это последовательность, содержащая не более 32 символов, среди которых могут быть любые буквы латинского алфавита a - z, A - Z, цифры 0 - 9 и знак подчеркивания (_). Первый символ идентификатора не должен быть цифрой.
Несмотря на то, что допускается имя, имеющее до 32 символов, определяющее значение имеют только первые 8 символов. Помимо имени, все данные имеют тип. Указание типа необходимо для того, чтобы было известно, сколько места в оперативной памяти будет занимать данный объект.
Компилятор языка Си придерживается строгого соответствия прописных и строчных букв в именах идентификаторов и лексем.
Верно | Неверно |
int a = 2, b; b = a+3; | Int a=2; // правильно int INT a=2; |
int a = 2, b; b = A + 3; // идентификатор А не объявлен | |
int a = 2; b = a + 3; // идентификатор b не объявлен |
Целочисленные данные могут быть представлены в знаковой и беззнаковой форме.
Беззнаковые целые числа представляются в виде последовательности битов в диапазоне от 0 до 2n-1, где n-количество занимаемых битов.
Знаковые целые числа представляются в диапазоне -2n-1...+2n-1-1. При этом старший бит данного отводится под знак числа (0 соответствует положительному числу, 1 – отрицательному).
Основные типы и размеры целочисленных данных:
Количество бит | Беззнаковый тип | Знаковый тип |
unsigned char 0...255 | char -128...127 | |
unsigned short 0...65535 | short -32768...32767 | |
unsigned int | int | |
unsigned long int | long int |
Вещественный тип предназначен для представления действительных чисел. Вещественные числа представляются в разрядной сетке машины в нормированной форме. Нормированная форма числа предполагает наличие одной значащей цифры (не 0) до разделения целой и дробной части. Такое представление умножается на основание системы счисления в соответствующей степени. Например, число 12345,678 в нормированной форме можно представить как
12345,678 = 1,2345678·104
Число 0,009876 в нормированной форме можно представить как
0,009876 = 9,876·10-3
В двоичной системе счисления значащий разряд, стоящий перед разделителем целой и дробной части, может быть равен только 1. В случае если число нельзя представить в нормированной форме (например, число 0), значащий разряд перед разделителем целой и дробной части равен 0.
Значащие разряды числа, стоящие в нормированной форме после разделителя целой и дробной части, называются мантиссой числа.
В общем случае вещественное число в разрядной сетке вычислительной машины можно представить в виде 4 полей.
знак - бит, определяющий знак вещественного числа (0 для положительных чисел, 1 - для отрицательных).
степень - определяет степень 2, на которую требуется умножить число в нормированной форме. Поскольку степень 2 для числа в нормированной форме может быть как положительной, так и отрицательной, нулевой степени 2 в представлении вещественного числа соответствует величина сдвига, которая определяется как
N-1
где n - количество разрядов, отводимых для представления степени числа.
целое - бит, который для нормированных чисел всегда равен 1, поэтому в некоторых представлениях типов этот бит опущен и принимается равным 1.
мантисса - значащие разряды представления числа, стоящие после разделителя целой и дробной части в нормированной форме.
Различают три основных типа представления вещественных чисел в языке Си:
Тип | Обозна- чение в Си | Кол-во бит | Биты степени | Мантисса | Сдвиг |
простое | float | 30...23 | 22...0 | ||
двойной точности | double | 62...52 | 51...0 | ||
двойной расширен- ной точности | long double | 78...64 | 62...0 |
Как видно из таблицы, бит целое у типов float и double отсутствует. При этом диапазон представления вещественного числа состоит из двух диапазонов, расположенных симметрично относительно нуля. Например, диапазон представления чисел типа float можно представить в виде:
Пример: представить число -178,125 в 32-разрядной сетке (тип float).
Для представления числа в двоичной системе счисления преобразуем отдельно целую и дробную части:
17810 = 101100102.
0,12510 = 0,0012.Тогда 178,12510 = 10110010,0012=1,0110010001·2111
(для преобразования в нормированную форму осуществляется сдвиг на 7 разрядов влево).
Для определения степени числа применяем сдвиг:
0111111+00000111 = 10000110.
Таким образом, число -178,125 представится в разрядной сетке как
Символьный тип хранит код символа и используется для отображения символов в различных кодировках. Символьные данные задаются в кодах и по сути представляют собой целочисленные значения. Для хранения кодов символов в языке Си используется тип char.
Подробнее о кодировке символов
Логический тип имеет два значения: истина и ложь - применяется в логических операциях, используется при алгоритмических проверках условий и в циклах.
В программе должно быть дано объявление всех используемых данных с указанием их имени и типа. Описание данных должно предшествовать их использованию в программе.
Пример объявления объектов
int n; // Переменная n целого типа
double a; // Переменная a вещественного типа двойной точности
Вопрос 2:Статистические члены в C++.Ограничения. Пример
Класс – это тип, а не объект данных, и в каждом объекте класса имеется своя собственная копия данных – членов этого класса. Однако некоторые типы требуется реализовать так, что все объекты этого типа могут совместно использовать некоторые данные. Такие совместно используемые данные должны быть описаны как часть класса.
Статические данные относятся ко всем объектам класса. Такие данные используются, если
· требуется контроль общего количества объектов класса;
· требуется одновременный доступ ко всем объектам или части их;
· требуется разделение объектами общих ресурсов.
В этом случае в определение класса могут быть введены статические члены.
Статические члены описываются с помощью ключевого слова static, которое может использоваться при объявлении член-данных и член-функций.
Такие члены классов называются статическими, и независимо от количества объектов данного класса, существует только одна копия статического элемента.
Обращение к статическому элементу осуществляется с помощью оператора разрешения контекста и имени класса:
ИмяКласса :: ИмяЭлемента
Если x – статическое член-данное класса cl, то к нему можно обращаться как
cl::x
При этом не имеет значения количество объектов класса cl.
Аналогично можно обращаться к статической член-функции:
class X {
public:
int n;
static void func(int v);
void f(int v);
...
};
int main() {
X obj;
obj.f(10) // для любой функция
X :: func(10); // только для статической функции
return 0;
}
Статические член-данные класса можно рассматривать как глобальную переменную класса. Но в отличие от обычных глобальных переменных на статические члены распространяются правила видимости private и public. Поместив статическую переменную в часть private, можно ограничить ее использование.
Объявление статического члена в объявлении класса не является определением, то есть это объявление статического члена не обеспечивает распределения памяти и инициализацию.
Инициализация статических член-данных
Статические члены-данные нельзя инициализировать в теле класса, а также в функциях-членах. Статические члены должны инициализироваться аналогично глобальным переменным в области видимости файла:
class example {
public:
static int mask;
};
int example::mask = 0; // определение и инициализация
Пример
#include <iostream>
using namespace std;
class temp {
static int number;
static char name[30];
int id;
public:
void setid(void);
int getid(void);
static char* getname(void);
};
int temp :: number = 0;
char temp :: name[] = "ClassName";
char* temp :: getname() {return(name); }
void temp :: setid(void) {
number++;
id = number;
}
int temp :: getid(void) {return(id);}
int main() {
temp a, b;
a.setid();
b.setid();
cout << "a.id = " << a.getid() << endl;
cout << "b.id = " << b.getid() << endl;
cout << temp::getname();
cin.get();
return 0;
}
Результат выполнения
Билет 14:
Вопрос 1:Динамическое распределение памяти в языке C.Примеры
В языке Си принято следующее распределение памяти:
Таблица 4
СТЕК | Верхние адреса |
СВОБОДНАЯ ПАМЯТЬ | |
РАЗДЕЛ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ И КОНСТАНТ | |
КОД ПРОГРАММЫ | Нижние адреса |
Для глобальных переменных отводится фиксированное место в памяти на все время работы программы. Локальные переменные хранятся в стеке. Между ними находится область памяти для динамического распределения.
Функции malloc( ) и free( ) используются для динамического распределения свободной памяти. Функция malloc( ) выделяет память, функция free( ) освобождает ее. Прототипы этих функций хранятся в заголовочном файле stdlib.h и имеют вид:
void *malloc(size_t size);
void *free(void *p);
Функция malloc( ) возвращает указатель типа void; для правильного использования значение функции надо преобразовать к указателю на соответствующий тип. При успешном выполнении функция возвращает указатель на первый байт свободной памяти размера size. Если достаточного количества памяти нет, возвращается значение 0. Чтобы определить количество байтов, необходимых для переменной, используют операцию sizeof( ).
Пример использования этих функций:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
int *p, i;
p = (int *) malloc(100 * sizeof(int)); /* Выделение памяти для 100
целых чисел */
if (!p)
{
printf("Недостаточно памяти\n");
exit(1);
}
for (i = 0; i < 100; ++i) *(p+i) = i; /* Использование памяти */
for (i = 0; i < 100; ++i) printf("%d", *(p++) );
free(p); /* Освобождение памяти */
}
Перед использованием указателя, возвращаемого malloc( ), необходимо убедиться, что памяти достаточно (указатель не нулевой).
Вопрос 2: Вызов конструкторов и деструкторов C++.Пример.
Конструктор - функция, предназначенная для инициализации объектов класса.Рассмотрим класс date:
class date {
int day, month, year;
public:
set(int, int, int);
};
Нигде не утверждается, что объект должен быть инициализирован, и программист может забыть инициализировать его или сделать это дважды.
ООП дает возможность программисту описать функцию, явно предназначенную для инициализации объектов. Поскольку такая функция конструирует значения данного типа, она называется конструктором. Конструктор всегда имеет то же имя, что и сам класс. Когда класс имеет конструктор, все объекты этого класса будут инициализироваться.
class date {
int day, month, year;
public:
date(int, int, int); // конструктор
};
Если конструктор требует аргументы, их следует указать:
date today = date(6,4,2014);
date xmas(25,12,0); // сокращенная форма (xmas - рождество)
// date my_burthday; // недопустимо, опущена инициализация
Если необходимо обеспечить несколько способов инициализации объектов класса, задается несколько конструкторов:
class date {
int month, day, year;
public:
date(int, int, int); // день месяц год
date(char*); // дата в строковом представлении
date(); // дата по умолчанию: сегодня
};
Конструкторы подчиняются тем же правилам относительно типов параметров, что и перегруженные функции. Если конструкторы существенно различаются по типам своих параметров, то компилятор при каждом использовании может выбрать правильный:
date july4("Февраль 27, 2014");
date guy(27, 2, 2014);
date now; // инициализируется по умолчанию
Одним из способов сократить количество перегруженных функций (в том числе и конструкторов) является использование значений по умолчанию.
Конструктор по умолчанию
Конструктор, не требующий параметров, называется конструктором по умолчанию. Это может быть конструктор с пустым списком параметров или конструктор, в котором все аргументы имеют значения по умолчанию.
Конструкторы могут быть перегруженными, но конструктор по умолчанию может быть только один.
class date {
int month, day, year;
public:
date(int, int, int);
date(char*);
date(); // конструктор по умолчанию
};
При создании объекта вызывается конструктор, за исключением случая, когда объект создается как копия другого объекта этого же класса, например:
date date2 = date1;
Однако имеются случаи, в которых создание объекта без вызова конструктора осуществляется неявно:
· формальный параметр – объект, передаваемый по значению, создается в стеке в момент вызова функции и инициализируется копией фактического параметра;
· результат функции – объект, передаваемый по значению, в момент выполнения оператора return копируется во временный объект, сохраняющий результат функции.
Во всех этих случаях транслятор не вызывает конструктора для вновь создаваемого объекта:
· date2 в приведенном определении;
· создаваемого в стеке формального параметра;
· временного объекта, сохраняющего значение, возвращаемое функцией.
Вместо этого в них копируется содержимое объекта-источника:
· date1 в приведенном примере;
· фактического параметра;
· объекта-результата в операторе return.
Конструктор копии
Конструктор копирования обязателен, если в программе используются функции-элементы и переопределенные операции, которые получают формальные параметры и возвращают в качестве результата такой объект не по ссылке, а по значению
При наличии в объекте указателей на динамические переменные и массивы или идентификаторов связанных ресурсов, такое копирование требует дублирования этих переменных или ресурсов в объекте-приемнике. С этой целью вводится конструктор копии, который автоматически вызывается во всех перечисленных случаях. Он имеет единственный параметр-ссылку на объект-источник:
class String {
char *str;
int size;
public:
String(String&); // Конструктор копирования
};
String::String(String& right) { // Создает копии динамических
// переменных и ресурсов
s = new char[right->size];
strcpy(str,right->str);
}
Деструктор
Определяемый пользователем класс имеет конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие. Деструктор обеспечивает соответствующую очистку объектов указанного типа. Имя деструктора представляет собой имя класса с предшествующим ему знаком "тильда" ~. Так, для класса X есть ~X(). Многие классы используют динамическую память, которая выделяется конструктором, а освобождается деструктором.
class date {
int *day, *month, *year;
public:
date(int d, int m, int y) {
day = new int;
month = new int;
year = new int;
*day = d;
*month = m;
*year = y;
}
~date() { delete day; delete month; delete year; }
};