
- •142) Концепция типа данных
- •144) Указатели предназначены для хранения адресов областей памяти.
- •Int I; // целая переменная
- •Int * pi; // указатель на целую переменную
- •145) Указатель на функцию
- •150) Функция – это группа операторов, которые выполняют какую-то задачу.
- •Определение функции;
- •Вызов функции.
Int I; // целая переменная
const int ci = 1; // целая константа
Int * pi; // указатель на целую переменную
const int * pci; // указатель на целую константу
int * const ср = &i; // указатель-константа на целую переменную
const int * const срс = &ci; // указатель-константа на целую константу
145) Указатель на функцию
Указатель на функцию содержит адрес в сегменте кода, по которому располагается исполняемый код функции, то есть адрес, по которому передается управление при вызове функции. Указатели на функции используются для косвенного вызова функции (не через ее имя, а через обращение к переменной, хранящей ее адрес), а также для передачи имени функции в другую функцию в качестве параметра. Указатель функции имеет тип «указатель функции, возвращающей значение заданного типа и имеющей аргументы заданного типа»:
тип (*имя) ( список_типов_аргументов );
Например, объявление:
int (*fun) (double, double);
задает указатель с именем fun на функцию, возвращающую значение типа int и имеющую два аргумента типа double.
146) Понятие структуры расширено в С++ так, что появилась возможность вводить закрытые (private) и открытые (public) члены. Это касается как членов данных, так и функций-членов структуры.
К открытым членам структуры имеется непосредственный доступ из лю-бой функции, имеющей доступ к объекту структуры.
К закрытым членам имеют доступ не все функции, а только те, чьи полномочия включают право доступа к этим членам. Таковыми являются функции-члены структур.
Полезно рассматривать закрытую часть, как код, доступный только разработчику, а открытую часть – как описание интерфейса, который используется клиентами. Разработчик может изменить закрытую часть, и это не повлияет на правильность использования структуры клиентом. То есть при изменении закрытой части структуры не нужно переписывать код, который использует эту структуру (хотя может потребоваться его перекомпиляция).
В открытую часть рекомендуется помещать только функции-члены, но не члены данных. В этом случае открытые функции-члены образуют интерфейс структуры (класса). В закрытой части можно помещать и функции, и данные. В этом случае для того, чтобы научиться пользоваться классом (структурой), его потенциальному пользователю необходимо ознакомиться только с определениями открытых функций-членов.
Другое преимущество – локализация ошибок. Неправильное значение закрытого члена данных может быть вызвано только неверным кодом функций-членов.
Классы являются формой структуры, у которой право доступа по умолчанию закрытое. Таким образом, struct и class взаимозаменяемы при условии надлежащего определения прав доступа.
147) C++ предоставляет набор классов файловых потоков, с помощью которых можно очень легко выполнять операции ввода и вывода (В/В) с файлами.
Используя выходной файловый поток, вы можете писать информацию в файл с помощью оператора вставки (<<).
Используя входной файловый поток, вы можете читать хранимую в файле информацию с помощью оператора извлечения (>>).
Для открытия и закрытия файла вы используете методы файловых классов.
Для чтения и записи файловых данных вы можете использовать операторы вставки и извлечения, а также некоторые методы файловых классов.
148) Чтобы, например, указать ширину полей, левое или правое выравнивание и т.д. можно использовать printf(), а можно сделать то же самое при помощи средств форматирования ввода/вывода языка С++. Существуют два способа форматирования ввода/вывода.
Первый способ предусматривает использование функций для установки определенных флагов форматирования, которые перечислены в классе ios_base
enum { skipws = 0x0001,
left = 0x0002,
right = 0x0004,
internal = 0x0008,
dec = 0x0010,
oct = 0x0020,
hex = 0x0040,
showbase = 0x0080,
showpoint = 0x0100,
uppercase = 0x0200,
showpos = 0x0400,
scientific = 0x0800,
fixed = 0x1000,
unitbuf = 0x2000,
stdio = 0x4000
};
Рассмотрим что делают перечисленные выше флаги.
Флаг Описание
skipws - Следующие в начале символы-разделители (пробелы, символы табуляции и новой строки) при выполнении ввода отбрасываются. |
left -Осуществляется ввод с левым выравниванием |
right Осуществляется ввод с правым выравниванием (по умолчанию) |
internal -Знак или префикс системы счисления выравнивает слева, число выравнивает справа |
dec- Численные значения выводятся в десятичной форме (по умолчанию) |
oct -Численные значения выводятся в восьмеричной форме |
hex -Численные значения выводятся в шестнадцатиричной форме |
showbase - Позволяет при выводе указывать числовую базу (десятичную, восьмеричную, шестнадцатиричную) |
showpoint - Приводит к выводу десятичной запятой и нулей справа для всех чисел с плавающей запятой вне зависимости, нужно это или нет |
uppercase - При выводе чисел с плавающей точкой при использовании экспоненциальной формы выводится большая буква E. Также при выводе чисел в шестнадцатиричной форме символ 'x' выводится в верхнем регистре. |
showpos - Приводит к тому, что перед положительным числом будет выводится знак "+" |
scientific - Числа с плавающей запятой выводятся с использованием научной нотации |
fixed - Числа с плавающей запятой выводятся в нормальной нотации |
unitbuf - При каждой операции вставки вывода данные немедленно заносится в поток |
Что делают флаги мы рассмотрели. Теперь необходимо познакомиться с функциями, которые устанавливают и сбрасывают эти флаги.
Первая функция, которую мы рассмотрим, - это функция setf(), используемая для установки флагов
long setf(long flags);
Функция принимает в качестве параметра рассмотренные выше флаг или флаги, соединенные между собой при помощи побитового ИЛИ. Она возвращает предыдущее значение флага. Рассмотрим пример:
cout.setf(ios::hex);
cout.setf(ios::showpos);
cout<<123<<" "<<123.45<<"\n";
cout<<67<<" "<<678.9<<"\n";
выведет на экран
7b +123.45
43 +678.9
тоже самое будет на экране после выполнения следующего кода
cout.setf(ios::hex | ios::showpos);
cout<<123<<" "<<123.45<<"\n";
cout<<67<<" "<<678.9<<"\n";
Для отключения установленных флагов нужно использовать функцию unsetf(). Она имеет следующий прототип:
long unsetf(long flags);
Функция возвращает значение предыдущей установки флага и сбрасывает флаги, определяемые параметром flags. Пример:
cout.setf(ios::showpos | ios::hex);
cout<<123<<" "<<123.45<<"\n";
cout.unsetf(ios::showpos | ios::hex);
cout<<123<<" "<<123.45<<"\n";
выведет на экран
7b +123.45
123 123.45
Кроме флага форматирования также можно установить ширину поля потока, символ для заполнения и число цифр после десятичной запятой. Для этого используются следующие функции:
int width(int len);
char fill(char ch);
int precision(int num);
Функция width() устанавливает ширину поля и возвращает текущую ширину. Вторая функция - fill() устанавливает текущий символ заполнения и возвращает предыдущий символ заполнения. По умолчанию используется пробел. А функция precision устанавливает точность чисел с плавающей точкой. В научном режиме эта функция определяет количество цифр после десятичной запятой. В обычной нотации функция обозначает количество выводимых цифр. Точность, установленная по умолчанию, равна 6. Функция width() воздействует только на один вывод информации, а после этого в действие вступают параметры принятые по умолчанию. В случае с остальными двумя функциями новые установки остаются действовать до их явного обновления. Приведем пример:
cout.width(10);
cout.fill('*');
cout<<123<<"\n";
cout.width(10);
cout<<456<<"\n\n";
cout<<"Using precision\n";
float price1 = 20.405f;
float price2 = (float)1.9+8.0/9.0;
cout<<price1<<"\n"<<price2<<"\n\n";
cout.precision(2);
cout<<price1<<"\n"<<price2<<"\n\n";
cout.setf(ios::scientific);
cout<<price1<<"\n"<<price2<<"\n\n";
выведет на экран
*******123
*******456
Using precision
20.405
2.78889
20
2.8
2.04e+001
2.79e+000
А теперь мы переходим к рассмотрению второго способа форматирования ввода/вывода. Второй способ - это использование манипуляторов.
Манипуляторы являются специальными функциями, которые позволяют изменять флаги потока. Существуют манипуляторы с параметрами и без. Если Вы используете манипуляторы с параметрами, подключите файл iomanip.h
Рассмотрим манипуляторы без параметров:
ends - помещает в выходной поток нулевой символ;
endl помещает в выходной поток символ конца строки и вызывает метод flush;
flush выгружает буфер потока;
dec, hex, oct устанавливают основания 10, 16 и 8 соответственно;
ws заставляет игнорировать ведущие пробелы при вводе.
Манипуляторы с параметрами
setbase(int b) задает основание системы счисления;
resetiosflags(long f) сбрасывает флаги, указанные в параметре;
setiosflags(long f) устанавливает флаги, указанные в параметре;
setfill(int ch) задает заполняющий символ;
setprecision(int n) задает точность вещественных чисел;
setw(int n) задает ширину поля.
Использование манипуляторов рассмотрим на примере
cout<<setw(5)<<setfill('*')<<22<<endl;
cout<<hex<<11<<endl;
выведет на экран
***22
b
149) Функторы в C++ являются сокращением от "функциональные объекты". Функциональный объект является экземпляром класса С++, в котором определён operator(). Если вы определите operator() для C++ класса, то вы получите объект, который действует как функция, но может также хранить состояние. Например,
#include <iostream>
#include <string>
class SimpleFunctor {
std::string name_;
public:
SimpleFunctor(const char *name) : name_(name) {}
void operator()() { std::cout << "Oh, hello, " << name_ << endl; }
};
int main() {
SimpleFunctor sf("catonmat");
sf(); // выводит "Oh, hello, catonmat" }
Обратите внимание, что мы можем вызывать sf() в функции main, хотя sf является объектом. Это потому, что в классе SimpleFunctor для него определён operator().
Чаще всего функторы в С++ используются в качестве предикатов, псевдозамыканий или функций сравнения в алгоритмах STL. Вот вам ещё один пример. Предположим, у вас есть список целых чисел и вы хотите найти сумму всех четных и сумму всех нечетных. Идеальная задача для функтора и for_each.
#include <algorithm>
#include <iostream>
#include <list>
class EvenOddFunctor {
int even_;
int odd_;
public:
EvenOddFunctor() : even_(0), odd_(0) {}
void operator()(int x) {
if (x%2 == 0) even_ += x;
else odd_ += x;
}
int even_sum() const { return even_; }
int odd_sum() const { return odd_; }
};
int main() {
EvenOddFunctor evenodd;
int my_list[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
evenodd = std::for_each(my_list, my_list+sizeof(my_list)/sizeof(my_list[0]), evenodd);
std::cout << "Сумма чётных: " << evenodd.even_sum() << "\n";
std::cout << "Сумма нечётных: " << evenodd.odd_sum() << std::endl;
// вывод:
// Сумма чётных: 30
// Сумма нечётных: 25
}
Здесь экземпляр EvenOddFunctor передается в for_each. for_each итерируется по каждому элементу в my_list и вызывает функтор. После этого он возвращает копию функтора evenodd, который содержит сумму чётных и нечётных элементов.