Слайд 1Новые возможности языка Си++
Слайд 2Операторы new и delete
В состав языка вошли операторы new и
delete, осуществляющие работу с динамической памятью на уровне языка
Оператор new
выделяет память под элемент или массив элементов
Тип *p = new Тип()
Тип *p = new Тип(инициализатор,...)
Тип *p = new Тип[кол-во элементов]
Оператор delete освобождает память, выделенную ранее оператором new
delete pObject;
delete [] pArray;
Слайд 3Возможность объявления переменной при её первом использовании
В отличие от языка
C, локальные переменные в C++ могут быть объявлены в любом
месте функции, а не только в начале блока
Более того, это улучшает читаемость программы и является необходимым в ряде случаев
Неизменным остается правило – переменная должна быть объявлена ДО ее первого использования
Есть возможность объявления переменной цикла непосредственно в операторе for
for (int i = 0; i < 10; ++i)
Время жизни такой переменной ограничено телом цикла (не соблюдается некоторыми старыми компияторами)
Слайд 5Перегрузка функций
Перегрузкой имени функции является его использование для обозначения разных
операций над разными типами
Если несколько функций выполняют одно и то
же действие с объектами различных типов, имеет смысл дать им одинаковые имена
Слайд 6Пример
#include
void Print(int number)
{
printf("%d", number);
}
void Print(double number)
{
printf("%.15f", number);
}
void Print(const char*
str)
{
printf("%s", str);
}
int main()
{
Print(1);
printf("\n");
Print(8.0/3);
printf("\n");
Print("Hello World\n");
return 0;
}
Слайд 7Функции с различным количеством аргументов
Функции с одним именем могут иметь
различное число аргументов
В этом случае, будет вызвана та функция, количество
формальных аргументов которой совпадает с количеством переданных параметров
Слайд 8Пример
void Print(char ch)
{
putchar(ch);
}
void Print(char ch, int count)
{
while (count--)
{
putchar(ch);
}
}
int main()
{
Print('!');
putchar('\n');
Print('!', 5);
putchar('\n');
return
0;
}
Слайд 9Выбор нужной функции
Для выбора функции из имеющихся вариантов компилятор сравнивает
типы фактических аргументов, указанные в вызове, с типами формальных аргументов
всех описаний функций с данным именем
В результате вызывается та функция, у которой типы формальных аргументов наилучшим образом сопоставились с параметрами вызова
Типы возвращаемых значений функций не учитываются
Слайд 10Правила сопоставления параметров
Правила сопоставления имеют следующие приоритеты (в порядке убывания):
Точное
сопоставление
сопоставление произошло без всяких преобразований типа или только с неизбежными
преобразованиями
Сопоставление с использованием стандартных целочисленных преобразований и преобразований с плавающей запятой
char в int, short в int, float в double
Сопоставление с использованием стандартных преобразований, определенных в §R.4
int в double, derived * в base*, unsigned в int
Сопоставление с использованием пользовательских преобразований §R.12.3
Сопоставление с использованием эллипсиса ... в описании функции
Выбирается та функция, у которой произошло сопоставление по наиболее приоритетному правилу
Слайд 11Возможные проблемы
Если две или более функций с данным именем были
сопоставлены по самому приоритетному правилу, компилятор выдаст сообщение об ошибке
В
этом случае требуется явно приводить аргументы к требуемым типам
Слайд 12Пример
void Print(int number)
{
printf("%d", number);
}
void Print(double number)
{
printf("%.15f", number);
}
int main()
{
unsigned int arg
= 2;
Print(arg); // ошибка
Print(static_cast(arg)); // OK
return 0;
}
Слайд 13Стандартные значения параметров
Некоторые функции могут принимать больше параметров, чем в
самых простых и часто используемых случаях
Для гибкого использования этих функций
могут применяться необязательные параметры
Слайд 14#include
void ClearArray(int *array, unsigned size, int fillValue = 0)
{
for
(unsigned index = 0; index < size; ++index)
array[index] = fillValue;
}
void
PrintArray(const int * array, unsigned size)
{
printf("{");
for (unsigned index = 0; index < size; ++index)
{
printf("%d", array[index]);
if (index != (size - 1)) printf(" ");
}
printf("}\n");
}
int main()
{
int arr[10];
unsigned const NUMBER_OF_ELEMENTS = sizeof(arr) / sizeof(*arr);
ClearArray(arr, NUMBER_OF_ELEMENTS);
PrintArray(arr, NUMBER_OF_ELEMENTS);
ClearArray(arr, NUMBER_OF_ELEMENTS, 1);
PrintArray(arr, NUMBER_OF_ELEMENTS);
return 0;
}
OUTPUT:
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
Слайд 15Ссылки
Ссылку можно рассматривать как еще одно имя объекта
Синтаксис
& означает
ссылку на
Применение
Задание параметров функций
Перегрузка операций
Слайд 16Ссылки в качестве параметров функций
При передаче параметра в функцию по
ссылке, функция принимает не копию аргумента, а ссылку на него
При
сложных типах аргументов это может дать прирост в скорости вызова функции
Измененное внутри функции значение формального параметра приведет к изменению значения переданного аргумента
Использование ссылок может быть альтернативным способом возврата значения из функции
Если функция не изменяет значение аргумента, будет иметь смысл передать его по константной ссылке
Слайд 17Пример 1
#include
void Swap(int & a, int & b)
{
int tmp
= a;
a = b;
b = tmp;
}
int main()
{
int a = 1,
b = 3;
pritnf(“a=%d, b=%d\n”, a, b);
Swap(a, b);
pritnf(“a=%d, b=%d\n”, a, b);
}
OUTPUT
a=1, b=3
a=3, b=1
Слайд 18Пример 2
struct Point
{
int x, y;
};
void Print(Point const& pnt)
{
printf("(x:%d, y:%d)\n", pnt.x,
pnt.y);
}
int main()
{
Point pnt = {10, 20};
Print(pnt);
return 0;
}
Слайд 19Инициализация ссылки
Ссылка должна быть обязательно проинициализирована
Должен существовать объект на который
она ссылается
Синтаксис
Тип & идентификатор = значение;
Инициализация ссылки совершенно отличается от
операции присваивания
Будучи проинициализированной, присваивание ссылке нового значения изменяет значение ссылаемого объекта, а не значение ссылки
Слайд 20Пример
#include
int main()
{
int i = 1;
int j = 3;
// инициализация
ссылки
int & ri = i;
printf("i=%d, j=%d\n", i, j);
// присваивание значения
объекту, на который ссылается ri
ri = j;
printf("i=%d, j=%d\n", i, j);
}
OUTPUT
i=1, j=3
i=3, j=3
Слайд 21Ссылки на временные объекты
При инициализации ссылки объектом другого типа компилятор
создает временный объект нужного типа и использует его для инициализации
ссылки
На данный временный объект может ссылаться только константная ссылка
То же самое происходит при инициализации ссылки значением константы
Изменение значения объекта в данном случае не отражается на значении временного объекта
Слайд 22Пример
int a = 1;
int & refA = a;
printf("a = %d\n",
a);
++refA;
printf("Now a = %d\n\n", a);
const double & refDoubleA = a;
printf("refDoubleA
= %f\n", refDoubleA);
++a;
printf("Now a = %d, refDoubleA = %f\n", a, refDoubleA);
OUTPUT:
a = 1
Now a = 2
refDoubleA = 2.00000
Now a = 3, refDoubleA = 2.00000
Слайд 23Пространства имен
Пространства имен позволяют логически сгруппировать классы, переменные и функции
в некоторые именованные области
Позволяют избежать конфликта имен идентификаторов в различных
модулях проекта
Разбивают программу на функциональные единицы
Слайд 24#include
namespace math
{
int calculateX2(int x)
{
return x * x;
}
}
namespace graphics
{
namespace shapes
{
struct
rectangle
{
int x, y, w, h;
};
struct circle
{
int x, y, r;
};
}
}
namespace sound_player
{
void
PlaySound()
{
// sound playing code is placed here
}
}
using namespace sound_player;
int main()
{
int x = 5;
int x2 = math::calculateX2(x);
graphics::shapes::rectangle rect = {0, 0, 40, 30};
PlaySound();
return 0;
}
Слайд 25Стандартная библиотека шаблонов (STL)
Программная библиотека, содержащая большое количество готового к
использованию обобщенного кода
Контейнеры
Итераторы
Алгоритмы
Все контейнеры, алгоритмы и итераторы в STL объявлены
в пространстве имен std
Стандарт запрещает программисту объявлять свои типы в данном пространстве имен
Слайд 26Контейнеры
Классы, предназначенные для хранения элементов определенного типа
STL содержит классы обобщенных
реализаций различных контейнеров, которые можно использовать с элементами различных типов
В
STL поддерживаются 2 вида контейнеров
Последовательные
Ассоциативные
Слайд 27Контейнеры в STL
Последовательные контейнеры
Строка (basic_string, string, wstring)
Вектор (vector)
Двусвязный список (list)
Двусторонняя
очередь (deque)
Ассоциативные контейнеры
карта (map, multimap)
множество (set, multiset)
Контейнеры-адаптеры
Стек (stack)
Очередь (queue)
Очередь с
приоритетом (priority_queue)
Слайд 28Строка std::string
Контейнер, предназначенный для хранения строк произвольной длины
В качестве элементов
строк могут выступать элементы типа char (string), wchar_t (wstring) или
определяемые пользователем типы (basic_string)
Данный контейнер автоматизирует задачу управления памятью, занимаемой символами строки и предоставляет набор методов для осуществления операций над строками
Для работы с данным классом строк необходимо подключить заголовочный файл
Слайд 29Пример
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
string hello
= “Hello”;
string world(“world”);
string helloWorld = hello + “ “ +
world;
size_t len = helloWorld.length();
printf(“%s\n”, helloWorld.c_str());
return 0;
}
Слайд 30Вектор std::vector
Контейнер для хранения динамического массива элементов произвольного типа
Как и
строка, вектор автоматизирует процесс управления памятью, занимаемой элементами массива
Везде, где
возможно, рекомендуется использовать класс vector как альтернативу динамическому выделению массивов объектов при помощи оператора new
К элементам массива предоставляется индексированный доступ
Для использования данного класса необходимо подключить заголовочный файл
Слайд 31Пример
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
// создаем
массив целых чисел, содержащий 100 элементов
vector vectorOfInt(100);
vector vectorOfString;
vectorOfInt.push_back(10);
vectorOfString.push_back(“Hello”);
std::string hello =
vectorOfString[0];
size_t numberOfItems = vectorOfString.size();
return 0;
}
Слайд 32Двусвязный список std::list
Реализовывает двусвязный список элементов произвольного типа
К элементам списка
осуществляется последовательный доступ при помощи итераторов
Вставка и удаление элементов из
произвольного места списка осуществляется за постоянное время
Для начала работы с данным контейнером необходимо подключить заголовочный файл
Слайд 33Пример
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
list
listOfStrings;
listOfStrings.push_back(“One”);
listOfStrings.push_back(“Two”);
listOfStrings.push_back(“Three”);
for (list::iterator it = listOfStrings.begin();
it != listOfStrings.end(); ++it)
{
std::string const& item
= *it;
cout << item << “, “;
}
return 0;
}
Слайд 34Двусторонняя очередь std::deque
Аналогична вектору, но обеспечивает эффективную вставку и удаление
элементов не только в конце, но и в начале очереди
Для
использования необходимо подключить заголовочный файл
Слайд 35Классы std::map и std::multimap
Ассоциативный контейнер, хранящий пары «ключ» - «значение»
Позволяет
отображать элементы одного типа в элементы другого или того же
самого типа
map – все ключи уникальные
multimap – допускается дублирование ключей
Для подключения данных классов необходимо подключить заголовочный файл
Слайд 36Пример
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
map
string> dictionary;
map.insert(pair(“Cat”, “Кошка”));
map[“Dog”] = “Собака”;
map[“Mouse”] = “Мышь”;
cout
<< “\n”;
return 0;
}
Слайд 37Классы множеств std::set и std::multiset
Ассоциативный контейнер, хранящий множество элементов определенного
типа
set – дублирование элементов не допускается
multiset – дублирование элементов допускается
Для
использования данных классов необходимо подключить заголовочный файл
Слайд 38Пример
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
set
primeNumbers;
primeNumbers.insert(2);
primeNumbers.insert(3);
primeNumbers.insert(5);
if (primeNumbers.find(3) != primeNumbers.end())
{
cout
maleNames;
maleNames.insert(“John”);
maleNames.insert(“Peter”);
return 0;
}
Слайд 39Итераторы
Итератор – объект, позволяющий программисту осуществлять перебор элементов контейнера вне
зависимости от деталей его реализации
Например, осуществлять вставку диапазона элементов одного
контейнера в другой
Итераторы используются в STL для доступа к элементам контейнеров
Обобщенные реализации алгоритмов используют итераторы для обработки элементов контейнеров
Итератор – связующее звено между контейнером и алгоритмом
Слайд 40Алгоритмы
Обобщенных функции, реализующие типичные алгоритмы над элементами контейнеров
Сортировка, поиск, поэлементная
обработка
Алгоритмы в STL не работают с контейнерами напрямую
Вместо этого алгоритмы
используют итераторы, задающие определенные элементы или диапазоны элементов контейнера
Для работы с алгоритмами STL необходимо подключить заголовочный файл
Слайд 41Пример
#include
#include
#include
#include
using namespace std;
int main(int argc, char
*argv[])
{
vector names;
names.push_back(“Peter”);
names.push_back(“Ivan”);
names.push_back(“John”);
list namesList;
sort(names.begin(), names.end());
copy(names.begin(), names.end(), namesList.end());
return 0;
}