Изучение языка C++


Чтобы посмотреть презентацию с картинками, оформлением и слайдами, скачайте ее файл и откройте в PowerPoint на своем компьютере.
Текстовое содержимое слайдов презентации:

Изучение языка C++: с чем придется иметь дело C++ процедурный язык, представленный С; объектно-ориентированный язык, представленный расширениями в форме классов, которые C++ добавляет к С; обобщенное программирование, поддерживаемое шаблонами C++. В курсе рассматриваются как элементы языка С, лежащего в основе C++, так и новые компоненты. рассмотрение функциональных возможностей, общих для обоих языков; различия между C++ и С;суперструктура C++;шаблоны С++. Истоки языка C++ 70 гг прошлого столетия языки программирования С и Pascal способствовали зарождению эры структурного программированияязык С предлагал инструментальные средства для структурного программирования, с его помощью можно было создавать компактные быстро выполняющиеся программы, а также справляться с решением аппаратных задач, например, с управлением коммуникационными портами и дисковыми накопителями. Наряду с ростом популярности языка С зарождалась и новая парадигма программирования: объектно-ориентированное программирование (ООП), которое было реализовано в таких языках, как SmallTalk и C++. Язык программирования С Деннис Ритчи (Dennis Ritchie), сотрудник компании Bell Laboratories, участвовал в проекте по разработке операционной системы Unix. Использовался язык ассемблера, тесно связанный с внутренним машинным языком, т.е. язык низкого уровня, который является специфическим для каждого процессора компьютера. ОС Unix предназначалась для работы на самых разнообразных типах компьютеров (или платформ). Таким образом, это предполагало использование языка программирования высокого уровня. Ритчи необходим был язык, который сочетал бы в себе эффективность языка низкого уровня и возможность доступа к аппаратным средствам с универсальностью и переносимостью языка высокого уровня. Поэтому, основываясь на старых языках программирования, он создал язык С. Философия программирования на языке С В общем случае компьютерные языки имеют дело с двумя концепциями — данные и алгоритмы. Данные — это информация, которую использует и обрабатывает программа. Алгоритмы — это методы, используемые программой.С - процедурный язык программирования, т.е. предполагает обработку данных с помощью алгоритмов. В ранних процедурных языках возникали организационные проблемы (goto). С:Структурное программирование (for, while, do while, if else, if then else). Нисходящее проектирование - разделение большой программы на небольшие задачи. Функции - программные модули, которые отвечают за выполнение конкретной задачи. Переход к C++: объектно-ориентированное программирование В отличие от процедурного программирования, в котором акцент делается на алгоритмах, в ООП во главу угла поставлены данные. Нужно создать такие формы данных, которые могли бы выразить важные характеристики решаемой задачи. Класс - спецификация, описывающая форму данных.Объект - индивидуальная структура данных, созданная в соответствии с классом. Восходящее программирование - процесс перехода с нижнего уровня организации, например, с классов, до верхнего уровня. Упрощение создания повторно используемого кода. Сокрытие информации - предотвращение несанкционированного доступа к данным. Полиморфизм для создания множества описаний для операций и функций. Наследование для создания новых классов на основе существующих классов. C++ и обобщенное программирование Обобщенное программирование направлено на упрощение повторного использования кода и на абстрагирование общих концепций.Акцент программирования ставится на независимость от конкретного типа данных. Можно один раз написать функцию для обобщенного (т.е. не указанного) типа и применять ее для множества существующих типов. Для этого в языке C++ предусмотрены шаблоны. Происхождение языка программирования C++ Язык C++ был создан в 80х гг в Bell Laboratories, где работал Бьярне Страуструп (Bjarne Stroustrup). Основа C++ - язык С. Принципы ООП позаимствованы в языке моделирования Simula67. Название языка C++ происходит от операции инкремента (++) в языке С, которая увеличивает на единицу значение переменной. Переносимость Чтобы сделать программу переносимой, нужно справиться с двумя проблемами. Первая — это разное оборудование. Вторая - несоответствие языков программирования. Национальный институт стандартизации США (American National Standards Institute — ANSI): в 1990 г. был сформирован комитет (ANSI X3J16), для разработки стандарта для языка программирования C++. Международная организация по стандартизации (International Organization for Standardization — ISO), комитет (ISO-WG-21). Стандарты 1998 г. С++98Описание существующих средств языка C++ и расширений языка: исключений, идентификации типов во время выполнения (Runtime Type Identification — RTTI), шаблонов и стандартной библиотеки шаблонов (Standard Template Library — STL). 2003 г. С++03Исправлены ошибки первого издания (ликвидированы опечатки, устранены неточности и т.п.), при этом средства языка программирования не менялись. 2011 г. С++11 Добавляет к языку множество средств. Удаление противоречий, а также упрощение изучения и применения C++. Не все компиляторы поддерживают С++11 Создание файла исходного кода Integrated Development Environment — IDEMicrosoft Visual C++, Embarcadero C++ Builder, Apple Xcode, Open Watcom C++, Digital Mars C++ и Freescale CodeWarrior.Текстовый редакторGNU C++ на платформах Unix и Linux, IBM XL C/C++ на платформе АІХ, а также в свободно распространяемых версиях компиляторов Borland 5.5, Digital Mars, поддерживаются только этапы компиляции и компоновки, и все команды должны вводиться в командной строке. Компиляция и компоновка Страуструп: cfront (сокращение от С front end), транслировала исходный код C++ в исходный код С.CCСС spiffy.C  spiffy.о  a. out GNU C++g++ spiffy.cxx  a. out Microsoft Visual C++ 2010 (Express)Проект Compile (Компилировать), Build (Построить), Make (Построить), Build All (Построить все), Link (Скомпоновать), Execute (Выполнить), Run (Выполнить), Debug (Отладить)Ошибки Литература Стивен Прата, Язык программирования C++. Лекции и упражнения, 6-е изд. Прохоренок Н.А. Программирование на С++ в Visual Studio® 2010 ExpressСтенли Липпман, Жози Лажойе, Барбара Му Язык программирования С++. Вводный курс. Четвёртое издание. Работа с данными Правила назначения имен переменным C++ Встроенные целочисленные типы C++Дополнения С++11 Файл climitsЧисловые литералы (константы) различных целочисленных типов Использование квалификатора const для создания символьных констант Встроенные типы с плавающей точкой C++ Файл cfloat, представляющий системные ограничения для различных типов с плавающей точкой Числовые литералы различных типов с плавающей точкой Арифметические операции C++ Автоматические преобразования типов Принудительные преобразования типов (приведения типов) Имена, назначаемые переменным Приветствуется назначение переменным осмысленных имен. Если переменная представляет стоимость поездки, то для нее следует выбрать такое имя, как cost_of_trip или costOfTrip, но не х или cot. В C++ необходимо придерживаться следующих простых правил именования. В именах разрешено использовать только алфавитных символов, цифр и символа подчеркивания (_). Первым символом имени не должна быть цифра. Символы в верхнем и нижнем регистре рассматриваются как разные. В качестве имени нельзя использовать ключевое слово C++. Имена, которые начинаются с двух символов подчеркивания или с одного подчеркивания и следующей за ним буквы в верхнем регистре, зарезервированы для использования реализациями C++, т.е. с ними имеют дело компиляторы и ресурсы. Имена, начинающиеся с одного символа подчеркивания, зарезервированы для применения в качестве глобальных идентификаторов в реализациях. На длину имени не накладывается никаких ограничений, и все символы в имени являются значащими. Однако некоторые платформы могут вводить свои ограничения на длину. Имена, назначаемые переменным int poodle; // допустимое int Poodle; // допустимое и отличающееся от poodle int POODLE; // допустимое и отличающееся от двух предыдущих Int terrier; // недопустимое — должно использоваться int, а не Int int my_stars3 // допустимое int _Mystars3; // допустимое, но зарезервированное — начинается с подчеркивания int 4ever; // недопустимое, потому что начинается с цифры int double; // недопустимое — double является ключевым словом C++ int begin; // допустимое — begin является ключевым словом Pascal int __fools; // допустимое, но зарезервированное — начинается с двух подчеркиваний int the_very_best_variable_i_can_be_version_112; // допустимое int honky-tonk; // недопустимое — дефисы не разрешены Целочисленные типы Базовые целочисленные типы, в порядке увеличения ширины: char, short, int, long, long long (С++11). Каждый имеет версии со знаком и без знака. • целочисленный тип short имеет ширину не менее 16 битов; • целочисленный тип int как минимум такой же, как short; • целочисленный тип long имеет ширину не менее 32 битов и как минимум такой же, как int; • целочисленный тип long long имеет ширину не менее 64 битов и как минимум такой же, как long. short score; // создает целочисленную переменную типа short int temperature; // создает целочисленную переменную типа int long position; // создает целочисленную переменную типа long sizeof(int); //возвращает размер типа или переменной в байтах. Заголовочный файл climits Определяет символические константы#define INT_MAX 32767 Инициализация int n_int = INT_MAX; int uncles =5; // инициализация uncles значением 5 int aunts = uncles; // инициализация aunts значением 5 int chairs = aunts + uncles + 4; // инициализация chairs значением 14 int wrens(432); // альтернативный синтаксис C++; wrens устанавливается в 432 C++11 int emus{7}; // устанавливает emus в 7 int rheas = {12}; // устанавливает rheas в 12 int rocs = {}; // устанавливает rocs в 0 int psychics{ }; // устанавливает psychics в 0 Типы без знаков, переполнение unsigned short change; unsigned int rovert; unsigned quarterback; unsigned long gone; unsigned long long pang; Что выбрать?Естественный размер -целочисленная форма, которую компьютер может обработать наиболее эффективным образом. intЧисло > 16 бит – long Целочисленные литералы (константы) Три системы счисления: с основанием 10: первая цифра находится в диапазоне 1-993 – это 93 (9*10+3)с основанием 8: первой цифрой является 0, а вторая цифра находится в диапазоне 1-7042 – это 34 (4*8+2)с основанием 16: первыми двумя символами являются 0х или 0Х. 0x42 – это 66 (4*16+2)0xF — это 150хА5 — это 165 (10*16 + 5). Определение компилятором C++ типа константы C++ хранит целочисленные константы в виде int, если только нет причины поступать по-другому. Причин может быть две: используется специальный суффикс, чтобы указать конкретный тип; значение слишком большое, чтобы его можно было уместить в int. Суффиксы - это буквы, размещаемые в конце числовой константы для обозначения типа константы. Суффикс l или L в целом числе означает, что оно относится к типу long, суффикс u или U определяет константу как unsigned int, суффикс ul, Ul, uL и UL обозначает константу unsigned long. Например, в системе, в которой используется 16-битный int и 32-битный long, число 22022 сохраняется в 16 битах как int, а число 22022L — в 32 битах как long. С++11 Cуффиксы ll и LL для типа long long, ull, Ull, uLL и ULL для типа unsigned long long. Тип char: символы и короткие целые числа ASCII (American Standard Code for Information Interchange) Литералы char 'А' соответствует 65, коду ASCII для символа А; 'а' соответствует 97, коду ASCII для символа а; '5' соответствует 53, коду ASCII для цифры 5; ' ' соответствует 32, коду ASCII для символа пробела; ' ! ' соответствует 33, коду ASCII для символа восклицательного знака. Универсальные символьные имена Универсальное имя символа начинается с последовательности \u или \U. За последовательностью \u следуют 8 шестнадцатеричных цифр, а за последовательностью \U — 16 шестнадцатеричных цифр. Эти цифры представляют код ISO 10646 для символа. int k\u00F6rper; cout << "Let them eat g\u00E2teau. \n"; Кодом ISO 10646 для символа ӧ является 00F6, а для символа в — 00Е2. Переменной будет присвоено имя kӧrper и отображена следующая строка: Let them eat gвteau. Расширенный тип символов wchar_t bob = L'P'; // символьная константа типа wchar_t wcout << L"tall" << endl; // вывод строки типа wchar_t С++11 char16_t, 16 битовchar32_t, 32 бита. Префикс для символьных и строковых констант char16_t, u’С’ u"be good". char32_t, U'R' U"dirty rat". char16_t ch1 = u'q'; // базовый символ в 16-битной форме char32_t ch2 = U'/U0000222B'; // универсальное имя символа в 32-битной форме Тип bool bool is_ready = true; Литералы true и false могут быть преобразованы в тип int, причем true преобразовывается в 1, a false в 0: int ans = true; // ans присваивается 1 int promise = false; // promise присваивается 0 Любое числовое значение или значение указателя может быть преобразовано неявно в значение bool. Любое ненулевое значение преобразуется в true, а нулевое значение — в false: bool start = -100; // start присваивается true bool stop =0; // stop присваивается false Квалификатор const const int Months = 12; // Months — это символическая константа для 12 Компилятор не позволяет в последующем изменять значение Months. const тип имя = значение; Неверно:const int toes; // значение toes в этот момент не определено toes = 10; // слишком поздно! Запись чисел с плавающей точкой 12.34 // число с плавающей точкой 939001.32 // число с плавающей точкой 0.00023 // число с плавающей точкой 8.0 // тоже число с плавающей точкой 2.52е+8 // можно использовать Е или е; знак + необязателен 8.33Е-4 // порядок может быть отрицательным 7Е5 // то же, что и 7.0Е+05 -18.32е13 // перед записью может стоять знак + или - 1.69е12 // внешний долг Бразилии в реалах 5.98Е24 // масса Земли в килограммах 9.11е-31 // масса электрона в килограммах Типы чисел с плавающей точкой float должен иметь, как минимум, 32 бита, double — как минимум, 48 битов и быть не меньше чем floatlong double должен быть минимум таким же, как и тип double. Файл cfloat // Минимальное количество значащих цифр #define DBL_DIG 15 // double #define FLT_DIG 6 // float #define LDBL_DIG 18 // long double // Количество битов, используемых для представления мантиссы #define DBL_MANT_DIG 53 #define FLT_MANT_DIG 24 #define LDBL_MANT_DIG 64 // Максимальные и минимальные значения порядка #define DBL_MAX_10_EXP +308 #define FLT_MAX_10_EXP +38 #define LDBL_MAX_10_EXP +4932 #define DBL_MIN_10_EXP -307 #define FLT_MIN_10_EXP -37 #define LDBL_MIN_10_EXP -4931 Арифметические операции в C++ • Операция + выполняет сложение операндов. 4 + 20 дает 24. • Операция - вычитает второй операнд из первого. 12-3 дает 9. • Операция * умножает операнды. 28*4 дает 112. • Операция / выполняет деление первого операнда на второй. 1000 / 5 дает 200. Если оба операнда являются целыми числами, то результат будет равен целой доли частного. Например, 17/3 дает 5, с отброшенной дробной частью. • Операция % находит остаток от деления первого операнда на второй. 19 % 6 равно 1, поскольку 6 входит в 19 три раза, с остатком 1. Приоритеты Преобразования типов • C++ преобразует значения во время присваивания значения одного арифметического типа переменной, относящейся к другому арифметическому типу. • C++ преобразует значения при комбинировании разных типов в выражениях. • C++ преобразует значения при передаче аргументов функциям. Проблемы Приведение типов (имяТипа) значение // преобразует значение в тип имяТипа имяТипа (значение) // преобразует значение в тип имяТипа int thorn(long) thorn // возвращает результат преобразования thorn в тип long long (thorn) // возвращает результат преобразования thorn в тип long cout « int('Q'); // отображает целочисленный код для ' Q' C++11 Указание auto вместо имени типа в инициализирующем объявленииauto n = 100; // n получает тип int auto x = 1.5; // х получает тип double auto у = 1.3e12L; // у получает тип long double Составные типы МассивыСтрокиКласс stringМетоды get() и getline()СтруктурыОбъединенияПеречисленияУказателиДинамическая памятьХранилищеКлассы vector и array Массивы Массив — это структура данных, которая содержит множество значений, относящихся к одному и тому же типу. Объявление массива должно описывать три аспекта: • тип значений каждого элемента; • имя массива; • количество элементов в массиве. short months[12]; // создает массив из 12 элементов типа short имяТипа имяМаccива[размерМаccива]; размерМассива, представляющее количество элементов, должно быть целочисленной константой, такой как 10, значением const либо константным выражением вроде 8 * sizeof(int)months[0] — это первый элемент массива months, a months[11] — его последний элемент. Инициализация массивов Только при объявлении массива. Нельзя присваивать один массив другому: int cards [4] = {3, 6, 8, 10}; // все в порядке int hand[4]; // все в порядке hand[4] = {5, 6, 7, 9}; //не допускается hand = cards; // не допускается Можно указывать меньше значений, чем в массиве объявлено элементов. float hotelTips[5] = {5.0, 2.5}; long totals[500] = {0}; short things [] = {1, 5, 3, 8}; Массивы в С++11 Можно отбросить знак =double earnings[4] {1.2е4, 1.6е4, 1.1е4, 1.7е4}; // допускается в С++11 Можно использовать пустые фигурные скобки: unsigned int counts[10] = {}; // все элементы устанавливаются в 0 float balances[100] {}; // все элементы устанавливаются в 0 Защита от сужения: long plifs[] = {25, 92, 3.0}; // не разрешено char slifs[4] {'h', 'i', 1122011, '\0'}; // не разрешено char tlifs[4] {'h‘, 'i', 112, 'NO'}; //разрешено Строки Строка — это серия символов, cохраненная в расположенных последовательно байтах памяти. Два способа работы со строками: в стиле С и класс stringСтроки в стиле С: последним в каждой строке является нулевой символ. char dog[8] = { 'b', 'e', 'a', 'u', 'х1, ' ', 'I', 'I'}; // это не строка char cat[8] = {'f, 'а', ' t', 'е', 's', 's', 'a', '\0'}; // а это - строка Более простой способ инициализации массива - строковой константой или строковым литералом: char bird[11] = "Mr. Cheeps"; // наличие символа \0 подразумевается char fish[] = "Bubbles"; // позволяет компилятору подсчитать // количество элементов Нужно обеспечить достаточный размер массива, чтобы в него поместились все символы строки, включая нулевой. Конкатенация строковых литералов cout << "I'd give my right arm to be" " a great violinist. \n"; cout << "I'd give my right arm to be a great violinist. \n" ; cout << "I'd give my right ar" "m to be a great violinist. \n"; #include char name1[200] = "Cowboy"; cout << strlen(name1) ; Построчное чтение ввода В классе istream, экземпляром которого является сіn, есть функции-члены, предназначенные для строчно-ориентированного ввода: getline() и get(). Функция getline () читает целую строку, используя символ новой строки, который передан клавишей , для обозначения конца ввода. cin.getline(name,20); //читает полную строку в массив name, предполагая, что строка состоит не более чем из 19 символов. Вместо того, чтобы прочитать и отбросить символ новой строки, get() оставляет его во входной очереди. cin.get(name, ArSize); // чтение первой строки cin.get () ; // чтение символа новой строки cin.get(dessert, Arsize) // чтение второй строки Конкатенация: cin.get(name, ArSize).get(); // конкатенация функций-членовcin.getline(namel, ArSize).getline(name2, ArSize); Класс string Можно применять переменные типа string (объекты класса)Класс string является частью пространства имен std#include #include // обеспечение доступа к классу string int main() { using namespace std; char charr1[20]; //создание пустого массива char charr2[20] = "jaguar"; // создание инициализированного массива string str1; // создание пустого объекта строки string str2 = "panther"; // создание инициализированного объекта строки Объект string можно использовать так же, как символьный массив. • Объект string можно инициализировать строкой в стиле С. • Чтобы сохранить клавиатурный ввод в объекте string, можно использовать сіn. • Для отображения объекта string можно применять cout. • Можно использовать нотацию массивов для доступа к индивидуальным символам, хранящимся в объекте string. Главное отличие между объектами string и символьными массивами: объект string объявляется как обычная переменная, а не массив. Инициализация строк в С++11 C++11 позволяет осуществлять списковую инициализацию для строк в стиле С и объектов string: char first_date[] = {"Le Chapon Dodu"}; char second_date[] {"The Elegant Plate"}; string third_date = {"The Bread Bowl"}; string fourth_date {"Hank's Fine Eats"}; Присваивание, конкатенация и добавление Объект string можно присвоить другому: char charr1[20]; // создание пустого массива char charr2[20] = "jaguar"; // создание инициализированного массива string str1; // создание пустого объекта string string str2 = "panther"; // создание инициализированной строки charr1 = charr2; // НЕ ПРАВИЛЬНО, присваивание массивов не разрешено str1 = str2; // ПРАВИЛЬНО, присваивание объектов допускается Упрощение комбинирования строк: str3 = strl + str2; // присвоить str3 объединение строк strl += str2; // добавить str2 в конец strl Другие формы строковых литералов в С++11 wchar_t title[] = L"Chief Astrogator"; // строка w_char char16_t name[] = u"Felonia Ripova"; // строка char_16 char32_t car[] = U"Humber Super Snipe"; // строка char_32 Необработанная (raw) строка: R" (….) "cout << R" (Jim "King" Tutt uses "\n" instead of endl.) " << '\n'; Эквивалент:cout << "Jim \"King\" Tutt uses \" \\n\" instead of endl." << '\n'; cout << R"+*("(Who wouldn't?)", she whispered.)+*" << endl; Cтруктуры Cтруктура может хранить элементы более чем одного типа. struct inflatable // объявление структуры { char name[20]; float volume; double price; }; После определения структуры можно создавать переменные этого типа: inflatable hat; // hat — структурная переменная типа inflatable Переменная hat имеет тип inflatable, для доступа к ее отдельным членам используется операция принадлежности или членства (.)hat.volume ссылается на член структуры по имени volumehat.price — на член по имени price. Члены hat. price и vincent .price являются эквивалентами переменных типа double и могут быть использованы точно так же, как любая другая переменная типа double. Короче говоря, hat является структурой, но hat.price относится к типу double. struct inflatable // объявление структуры { char name[20]; float volume; double price; }; int main () { using namespace std; inflatable guest = { "Glorious Gloria", // значение name 1.88, // значение volume 2 9.99' // значение value }; // guest — структурная переменная типа inflatable inflatable duck = {"Daphne", 0.12, 9.98}; duck = guest; Комбинирование определение формы структуры с созданием структурных переменных. struct perks { int key_number; char car[12]; } mr_smith, ms_jones; // две переменных типа perks Одновременная инициализация:struct perks { int key_number; char car[12]; } mr_glitz = { 7, // значение члена mr_glitz.key_number "Packard" // значение члена mr_glitz.car }; Массивы структур struct inflatable // объявление структуры { char name[20]; float volume; double price; }; inflatable gifts[100]; // массив из 100 структур inflatable cin >> gifts[0].volume; // используется член volume первой структуры cout << gifts[99].price << endl; //отображается член price последней структуры inflatable guests [2] = // инициализация массива структур { {"Bambi", 0.5, 21.99}, // первая структура в массиве {"Godzilla", 2000, 5 65.99} // следующая структура в массиве }; Объединения Объединение — это формат данных, который может хранить в пределах одной области памяти разные типы данных, но в каждый момент времени только один из них. Размер объединения определяется размером его самого большого члена. union one4all { int int_val; long long_val; double double_val; }; one4all pail; pail.int_val = 15; // сохранение int cout << pail.int_val; pail.double_val =1.38; // сохранение double, int теряется cout << pail.double_val; Перечисления enum - альтернативный по отношению к const способ создания символических констант. enum spectrum {red, orange, yellow, green, blue, violet, indigo, ultraviolet}; Объявляет имя нового типа — spectrum; при этом spectrum называется перечислением, почти так же, как переменная struct называется структурой. Устанавливает red, orange, yellow и т.д. в качестве символических констант для целочисленных значений 0-7. Эти константы называются перечислителями. spectrum band; // band — переменная типа spectrum band = blue; // правильно, blue - перечислитель band = 2000; // неправильно, 2000 — не перечислитель enum bits {one = 1, two = 2, four = 4, eight = 8}; enum bigstep{first, second = 100, third}; Каждое перечисление имеет диапазон. enum bits {one = 1, two = 2, four = 4, eight = 8}; bits myflag; myflag = bits (6); // правильно, потому что 6 находится в пределах диапазона Указатели Указатели представляют собой переменные, хранящие адреса значений вместо самих значений. Операция взятия адреса обозначается символом &int donuts = 6; cout « "donuts address = " « &donuts « endl; donuts address = 0x0065fd40 Применяя операцию *, называемую косвенным значением или операцией разыменования, можно получить значение, хранящееся в указанном месте. Объявление и инициализация указателей Объявление указателя должно задавать тип данных указываемого значения. int * p_updates; //p_update указывает на int. Инициализировать указатель можно в операторе объявления. int higgens = 5; int * pt = &higgens; Всегда инициализируйте указатель, чтобы определить точный и правильный адрес, прежде чем применять к нему операцию разыменования (*). long * fellow; // создать указатель на long *fellow = 223323; // поместить значение в неизвестное место Указатели и числа Указатели — это не целочисленные типыint * pt; pt = 0хВ8000000; // несоответствие типов Надо делать приведение типа:int * pt; pt = (int *) 0xB8000000; // теперь типы соответствуют Выделение памяти с помощью операции new Реальная ценность указателей проявляется тогда, когда во время выполнения выделяются неименованные области памяти для хранения значений. int * pn = new int; //рn указывает на объект данных Память, выделяемая операцией new, находится в области, называемой кучей или свободным хранилищем. Освобождение памяти с помощью операции delete Операция delete применяется с указателем на блок памяти, который был выделен операцией new: int * ps = new int; // выделить память с помощью операции new ... // использовать память delete ps; // по завершении освободить память с помощью операции delete Указатель при этом не удаляется. Создание динамических массивов Распределение массива во время компиляции называется статическим связыванием и означает, что массив встраивается в программу во время компиляции. Динамическое связывание означает, что массив будет создан во время выполнения программы. Такой массив называется динамическим массивом. int * psome = new int [10] ; // получение блока памяти из 10 элементов типа int ….delete [] psome; // освобождение динамического массива Арифметика указателей C++ интерпретирует имя массива как адрес его первого элемента. Cледующий оператор создает pw как указатель на тип double, затем инициализирует его wages, который также является адресом первого элемента массива wages: double * pw = wages; Для wages, как и любого другого массива, справедливо следующее утверждение: wages = &wages[0] = адрес первого элемента массива C++ выполняет следующее преобразование: имя_массива[і] превращается в * (имя_массива + і) C++ осуществляет преобразование: имя_указателя[і] превращается в *(имя_указателя + і) Т.е. имена указателей и имена массивов можно использовать одинаковым образом. Нотация квадратных скобок применима и там, и там. К обоим можно применять операцию разыменования (*). В большинстве выражений каждое имя представляет адрес. Единственное отличие состоит в том, что значение указателя изменить можно, а имя массива является константой: имя_указателя = имя_указателя + 1; // правильно имя массива = имя массива + 1; // не допускается Объявление и инициализация double * рn; // рn может указывать на значение double char * рс; // рс может указывать на значение char double * рn; // рn может указывать на значение double double * pa; // так же и ра char * рс; // рс может указывать на значение char double bubble = 3.2; pn = &bubble; // присваивание адреса bubble переменной рп pc = new char; // присваивание адреса выделенной памяти char переменной pc pa = new double [30] ; // присваивание адреса массива из 30 double переменной pa Разыменование указателей Разыменование указателя — это получение значения, на которое он указывает. Для этого к указателю применяется операция разыменования (*). cout « *рn; // вывод значения bubble *рс = 'S'; // помещение 'S' в область памяти, на которую указывает рс Нотация массивов — второй способ разыменования указателя; например, рn [ 0 ] — это то же самое, что и *рn. Никогда не следует разыменовывать указатель, который не был инициализирован правильным адресом. Указатели и строки char flower[10] = "rose"; cout << flower << "s are red\n"; Имя массива — это адрес его первого элемента, потому flower в операторе cout представляет адрес элемента char, содержащего символ г. Объект cout предполагает, что адрес char — это адрес строки, поэтому печатает символ, расположенный по этому адресу, и затем продолжает печать последующих символов до тех пор, пока не встретит нулевой символ (\0). Cтрока в кавычках, как и имя массива, служит адресом его первого элемента. Создание динамических структур inflatable * ps = new inflatable; // создание безымянной структуры типа inflatable Однако теперь нельзя применить операцию членства к имени структуры, поскольку эта структура безымянна. Все, что у вас есть — ее адрес. Операция членства через указатель -> ps->price означает член price структуры, на которую указывает ps Более неуклюжий подход: (*ps).price Автоматическое, статическое и динамическое хранилище Три способа управления памятью для данных — в зависимости от метода ее выделения: автоматическое хранилище, статическое хранилище и динамическое хранилище (свободное хранилище или куча). C++11 - четвертая форма, которая называется хранилищем потока Автоматическое хранилище Обычные переменные, объявленные внутри функции, используют автоматическое хранилище и называются автоматическими переменными. Этот термин означает, что они создаются автоматически при вызове содержащей их функции и уничтожаются при ее завершении. Автоматические переменные обычно хранятся в стеке. Это значит, что когда выполнение программы входит в блок кода, его переменные последовательно добавляются к стеку в памяти и затем освобождаются в обратном порядке, когда выполнение покидает данный блок. (Этот процесс называется LIFO (last-in, first-out — "последним пришел — первым ушел".) Таким образом, по мере продвижения выполнения стек растет и уменьшается. Статическое хранилище Статическое хранилище — это хранилище, которое существует в течение всего времени выполнения программы. Доступны два способа для того, чтобы сделать переменные статическими. Один заключается в объявлении их вне функций. Другой предполагает использование при объявлении переменной ключевого слова static: static double fee = 56.50; Динамическое хранилище Операции new и delete управляют пулом памяти, который в C++ называется свободным хранилищем или кучей. Этот пул отделен от области памяти, используемой статическими и автоматическими переменными. Время жизни данных при этом не привязывается жестко к времени жизни программы или функции. Совместное применение new и delete предоставляет возможность более тонко управлять использованием памяти, чем в случае обычных переменных. Однако управление памятью становится более сложным. Шаблонный класс vector Класс vector использует операции new и delete для управления памятью, но делает это автоматически. #include …using namespace std; vector vi; // создание массива int нулевого размера int n; cin >> n; vector vd(n); // создание массива из п элементов double Шаблонный класс array (C++11) Объект array имеет фиксированный размер и использует стек (или распределение в статической памяти) вместо свободного хранилища#include using namespace std; array ai; // создание объекта array из пяти элементов int array ad = {1.2, 2.1, 3.43. 4.3}; В отличие от vector, количество_элементов не может быть переменной. Введение в циклы for for (инициализация; проверочное выражение; обновляющее выражение) тело В C++ принят стиль помещения пробелов между for и последующими скобками, а также пропуск пробела между именем функции и следующими за ним скобками: for (int і = 6; і < 10; і++) smart_function(i); Операции инкремента и декремента Префиксная версия операции указывается перед операндом, как в ++х. Постфиксная версия следует после операнда, как в х++. int х = 5; int у = ++х; // изменить х, затем присвоить его у // у равно 6, х равно 6 int z = 5; int у = z++; // присвоить у, затем изменить z //у равно 5, z равно 6 Префиксный инкремент, префиксный декремент и операция разыменования имеют одинаковый приоритет и ассоциируются слева направо. Постфиксный инкремент и декремент имеют одинаковый приоритет, более высокий, чем приоритет префиксных форм. Эти две операции также ассоциируются слева направо. double arr[5] = {21.1, 32.8, 23.4, 45.2, 37.4}; double *pt = arr; // pt указывает на arr[0], т.е. на 21.1 ++pt; // pt указывает на arr[1], т.е. на 32.8 double х = *++pt; // инкремент указателя, получение значения; // т.е. агг[2] , или 23.4 ++*pt; // инкремент указываемого значения; т.е. изменение 23.4 на 24.4 (*pt)++; // инкремент указываемого значения изменение 24.4 на 25.4 х = *pt++; // разыменование исходного указателя, затем инкремент указателя Составные операторы, или блоки for (int i = 1; i <= 5; i + + ) { // начало блока cout « "Value " « i « " : "; . // ввод числа cin >> number; sum += number; }// конец блока int x = 20; { int у = 100; cout << x << endl; cout « у « endl; // нормально } cout « x « endl; cout « у « endl; // ошибка во время компиляции! Oперация запятой Операция запятой (,) позволяет вставлять два выражения туда, где синтаксис C++ допускает только одно. string word; char temp; int i, j ; for (j = 0, i = word.size() - 1; j < i; --i, ++j) { // начало блока temp = word[i]; word[i] = word[j]; word[j] = temp; } // конец блока Выражения отношений < Меньше чем <= Меньше или равно == Равно > Больше чем >= Больше или равно ! = Не равно for (i = 0; quizscores [i] == 20; in-) cout << "quiz " << i << " is a 20\n"; for (i = 0; quizscores [i] = 20; i + + ) cout « "quiz " << i << " is a 20\n" ; Цикл while while (проверочное_условие) тело char name[ArSize]; cout << "Your first name, please: ”; cin » name; // ввод имени cout « "Here is your name, verticalized and ASCIIized:\n"; // Вывод имени посимвольно и в кодах ASCII int i = 0; // начать с начала строки while (name[i] != '\0') // обрабатывать до конца строки { cout « name[i] « " : " « int (name [i] ) « endl; i++; // не забудьте этот шаг } Цикл do while do тело while (проверочное-выражение) ;do { cin >> n; // выполнить тело } while (n != 7); // затем проверить Цикл for, основанный на диапазоне (С++11) double prices[5] = {4.99, 10.99, 6.87, 7.99, 8.49}; for (double x : prices) cout << x << std: :endl; // цикл отображает все значения, включенные в диапазон массива. Изменение значений в массиве:for (double &x : prices) х = х * 0.80; // скидка 20% Вложенные циклы и двумерные массивы Двумерный массив - массив, каждый элемент которого является массивом. int maxtemps[4][5]; int maxtemps[4][5] = // двумерный массив { {96, 100, 87, 101, 105}, // значения для maxtemps[0 ](96, 98, 91, 107, 104}, // значения для maxtemps[1] {97, 101, 93, 108, 107}, // значения для maxtemps[2] {98, 103, 95, 109, 108} // значения для maxtemps [3] };for (int row = 0; row < 4; row+ + ) { for (int col = 0; col < 5; ++col) cout << maxtemps [row] [col] << "\t"; cout << endl; } Операторы ветвления и логические операцииОператор if if (проверочное-условие) оператор if (проверочное-условие) оператор1 else оператор2 if (ch == 'Z') { // блок, выполняемый, если условие истинно zorro++; cout << "Another Zorro candidate\n"; } else { // блок, выполняемый, если условие ложно dull++; cout << "Not a Zorro candidate\n"; } Другой формат: if (ch == 'Z') { zorro++; cout << "Another Zorro candidate\n"; } else { dull++; cout << "Not a Zorro candidate\n"; } Логические выражения Три логических операции, с помощью которых можно комбинировать или модифицировать существующие выражения: логическое "ИЛИ" (записывается как | |), логическое "И" (записывается как &&) и логическое "НЕ" (записывается как !). Операции | | и && являются точкой следования. Логическая операция "И" имеет более высокий приоритет, чем логическая операция "ИЛИ". (age > 50 | | weight > 300) && donation > 1000 х != 0 && 1.0 / х > 100.0 Библиотека символьных функций cctype #include // прототипы символьных функций Проверка, что ch является буквенным символом: if ( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ) С применением функции isalpha() проще: if (isalpha(ch)) isalnum() Возвращает true, если аргумент — буква или десятичная цифра isalpha() Возвращает true, если аргумент — буква isblank() Возвращает true, если аргумент — пробел или знак горизонтальной табуляции iscntrl() Возвращает true, если аргумент — управляющий символ isdigit() Возвращает true, если аргумент — десятичная цифра (0-9) isgraph() Возвращает true, если аргумент — любой печатаемый символ, отличный от пробела islower() Возвращает true, если аргумент — символ в нижнем регистре isprint() Возвращает true, если аргумент — любой печатаемый символ, включая пробел ispunct() Возвращает t rue, если аргумент — знак препинания isspace() Возвращает true, если аргумент — стандартный пробельный символ (т.е. пробел, прогон страницы, новая строка, возврат каретки, горизонтальная табуляция, вертикальная табуляция) isupper() Возвращает true, если аргумент — символ в верхнем регистре isxdigit() Возвращает true, если аргумент — шестнадцатеричная цифра (т.е. 0-9, a-f или A-F) tolower() Если аргумент — символ верхнего регистра, возвращает его вариант в нижнем регистре, иначе возвращает аргумент без изменений toupper() Если аргумент — символ нижнего регистра, возвращает его вариант в верхнем регистре, иначе возвращает аргумент без изменений Операция ?: выражение1 ? выражение2 : выражениеЗ int a = 5 > 3 ? 10 : 12; // a = 10 int b = 3 == 9 ? 25 : 18; // b = 18 int c = a > b ? a : b; Аналог:int с; if (а > b) с = а; else с = b; Оператор switch switch (целочисленное-выражение) { case метка 1 : оператор (ы) break;case метка2 : оператор (ы) break;default : оператор (ы) } Использование перечислителей в качестве меток enum {red, orange, yellow, green, blue, violet, indigo}; …switch (code) { case red : cout << "Her lips were red.\n"; break; case orange : cout << "Her hair was orange. \n"; break; case yellow : cout << "Her shoes were yellow. \n"; break; case green : cout « "Her nails were green. \n"; break; case blue : cout << "Her sweatsuit was blue.\n"; break; case violet : cout << "Her eyes were violet. \n"; break; case indigo : cout << "Her mood was indigo. \n"; break; } Операторы break и continue Позволяют программе пропускать часть кода. Простой файловый ввод-вывод Запись в текстовый файл 1. Включить заголовочный файл fstream. #include 2. Создать объект ofstream. ofstream outFile; 3. Ассоциировать объект ofstream с файлом. outFile.open("carinfo.txt"); 4. Работать с объектом ofstream в той же манере, как с cout, принимая в расчет пространство имен stdoutFile << fixed; outFile « "Was asking $" « a_price « endl; 5. Когда работа с файлом завершена, соединение должно быть закрыто: outFile.close(); Чтение текстового файла 1. Включить заголовочный файл fstream. #include // поддержка файлового ввода-вывода 2.Принять в расчет пространство имен std; using namespace std; 3. Объявить одну или более переменных ifstreamifstream inFile; 4.Ассоциировать конкретный объект ifstream с конкретным файлом; inFile.open("bowling.txt"); 5. Использовать объект ifstream с операцией >> для чтения данных различных типов, либо с методом get() для чтения отдельных символов и с методом getline() — для чтения целых строк. double wt; inFile >> wt; char line[81]; inFile.getline(line, 81); Проверка того, удалось ли открыть файлinFile.open("bowling.txt"); if (!inFile.is_open()) //good() для старых компиляторов{ exit(EXIT_FAILURE); } Можно использовать объект ifstream с такими методами, как eof() и fail(), чтобы отслеживать успешность попыток ввода. if (inFile.eof()) // достигнут конец файла cout « "End of file reached.\n"; По завершении работы с файлом использовать метод close() для его закрытия. inFile.close (); // Стандартная структура цикла чтения inFile » value; // ввод первого значения while (inFile.good () ) // пока ввод успешен и не достигнут EOF { // Тело цикла inFile » value; // ввод следующего значения } // Сокращенная структура цикла чтения // Ввод перед циклом опущен while (inFile » value) // чтение и проверка успешности { // Тело цикла // Ввод в конце цикла опущен } Обзор функций Чтобы использовать функцию в C++, вы должны выполнить следующие шаги: • предоставить определение функции; • представить прототип функции; • вызвать функцию. #include void simple(); // прототип функции int main() { using namespace std; cout << "main() will call the simple() function: \n"; simple(); // вызов функции cout « "main() is finished with the simple() function.\n"; cin.get(); return 0; } // Определение функции void simple () { using namespace std; cout « "I'm but a simple function. \n"; } Функции, не возвращающие значений Функции типа void void имяФункции(списокПараметров) { оператор (ы) return; // не обязательно } void cheers(int n) // возвращаемое значение отсутствует { for (int i = 0; i < n; i++) std::cout « "Cheers! "; std::cout << std::endl; } Функции с возвращаемым значением имяТипа имяФункции(списокПараметров) {оператор (ы) return значение; // значение приводится к типу имяТипа }Возвращаемое значение не может быть массивом. int bigger (int a, int b) { if (a > b ) return a; // если a > b, функция завершается здесь else return b; // в противном случае функция завершается здесь } Зачем нужны прототипы? Прототип сообщает компилятору, каков тип возвращаемого значения, если оно есть у функции, а также количество и типы аргументов данной функции. В прототипе можно указывать или не указывать имена переменных в списке аргументов. double cube(double x) ; void cheers(int); Прототипирование происходит во время компиляции и называется статическим контролем типов. Аргументы функций В C++ аргументы обычно передаются по значению, т.е. числовое значение аргумента передается в функцию, где присваивается новой переменной. #include double cube(double x); // прототип: возвращает double int main() { int side = 5;double volume = cube(side); // вызов функции …} double cube(double x) { return x * x * x; } Переменные, включая параметры, объявленные в функции, являются приватными по отношению к этой функции. Когда функция вызывается, компьютер выделяет память, необходимую для этих переменных. Когда функция завершается, компьютер освобождает память, которая была использована этими переменными. Такие переменные называются локальными переменными, потому что они локализованы в пределах функции. Функции и массивы Язык C++, подобно С, в большинстве контекстов трактует имя массива как указатель. #include const int ArSize = 8; int sum_arr(int arr[], int n) ; int main() { using namespace std; int cookies[ArSize] = {1,2,4,8,16,32,64,128}; int sum = sum_arr(cookies, ArSize); cout « "Total cookies eaten: " « sum « "\n"; return 0; } int sum_arr(int arr[], int n) { int total = 0; for (int i = 0; i < n; i++) total = total + arr[i]; return total; } cookies == &cookies[0] // имя массива — это адрес его первого элемента Правильный заголовок функции должен быть таким: int sum_arr(int * arr, int n) // arr = имя массива, n = размер В C++ нотации int * arr и int arr [ ] имеют идентичный смысл, когда (и только когда) применяются в заголовке или прототипе функции. аrr[і] == * (аr + і) // значения в двух нотациях &arr[i] == ar+i // адреса в двух нотациях Наполнение массива Поскольку функция с аргументом — именем массива получает доступ к исходному массиву, а не к копии, можно использовать вызов функции для присваивания значений его элементам. Если назначение функции не предусматривает внесения изменений в переданные ей данные, нужно предохранить ее от этого, применяя ключевое слово const void show_array(const double ar[], int n) ; Тогда компилятор выдаст ошибку при попытке изменить массив. Указатели и const int age = 39; const int * pt = &age; Это объявление устанавливает, что pt указывает на const int (в данном случае — 39). Другими словами, значение *pt является константным и не может быть изменено: *pt += 1; // НЕПРАВИЛЬНО, потому что pt указывает на const int cin >> *pt; // НЕПРАВИЛЬНО по той же причине Однако можно изменить значение age непосредственно*pt = 20; // НЕПРАВИЛЬНО, потому что pt указывает на const int age = 20; // ПРАВИЛЬНО, потому что age не объявлено как const Функции и двумерные массивы int data[3] [4] = {{1,2,3,4}, {9,8,7,6}, {2,4,6,8}}; int total = sum(data, 3); data — это указатель на массив из четырех int int sum(int (*ar2) [4], int size); //прототипАльтернатива:int sum(int ar2[][4], int size); Количество столбцов является фиксированным и равно 4, число строк можно менять.int а[100][4]; int b[6][4]; int total1 = sum(a, 100); // сумма всех элементов а int total2 = sum(b, 6); // сумма всех элементов b Функции и структуры Передача структур по значениюstruct travel_time { int hours; int mins; }; travel_time sum(travel_time tl, travel_time t2) ; void show_time(travel_time t) ; int main () {using namespace std; travel_time day1 = {5, 45}; travel_time day2 = {4, 55}; travel_time trip = sum(day1, day2); cout « "Two-day total: "; show_time (trip) ; …} Передача адресов структур Для экономии времени и памяти можно передавать адрес структуры вместо самой структуры. void sum(const travel_time * t1, const travel_time * t2, const travel_time * t3) ; void show_time(const travel_time *t) ; int main () {using namespace std; travel_time day1 = {5, 45}; travel_time day2 = {4, 55}; travel_time trip; sum(&day1, &day2, &trip); cout « "Two-day total: "; show_time (&trip) ; …} Рекурсия Функция C++ обладает интересной характеристикой — она может вызывать сама себя. void recurs(списокАргументов) { Операторы1;if (проверка) recurs(аргументы);операторы2;} Указатели на функции Функции, как и элементы данных, имеют адреса. Можно написать функцию, которая принимает адрес другой функции в качестве аргумента. Если имеется функция think (), то ее адрес записывается как think. Чтобы передать функцию в качестве аргумента, нужно передать ее имя. process(think); // передача адреса think() функции process () thought(think()); // передача возвращаемого значения think() функции thought() double pam(int); // прототип Объявление соответствующего типа указателя: double (*pf)(int); // pf указывает на функцию, которая принимает один аргумент типа int и возвращает тип double Но:double *pf(int); // pf() — функция, возвращающая указатель на double double pam(int); double (*pf)(int); pf = pam; // pf теперь указывает на функцию pam() double x = pam(4); // вызвать pam(), используя ее имя double у = (*pf)(5); // вызвать pam(), используя указатель pf В действительности C++ позволяет использовать pf, как если бы это было имя функции: double у = pf(5); // также вызывает раm(), используя указатель pf Встроенные функции C++ Встроенные функции являются усовершенствованием языка C++, предназначенным для ускорения работы программ. Скомпилированный код такой функции непосредственно встраивается в код программы. Чтобы воспользоваться встроенной функцией, нужно выполнить хотя бы одно из следующих действий. • Предварить объявление функции ключевым словом inline. • Предварить определение функции ключевым словом inline. Общепринято опускать прототип и помещать полное описание (заголовок и весь код функции) туда, где обычно находится прототип. Если определение не помещается в одной или двух строках (предполагается, что длинные идентификаторы не используются), то такая функция, скорее всего, является плохим кандидатом на то, чтобы быть встроенной. #include // Определение встроенной функции inline double square (double x) { return x * x; } int main () { using namespace std; double a, b; double с = 13.0; a = square(5.0); … Ссылочные переменные Ссылка представляет собой имя, которое является псевдонимом, или альтернативным именем, для ранее объявленной переменной. int rats; int & rodents = rats; // rodents становится псевдонимом имени rats Но:int * prats = &rats; // prats - указатель Ссылки как параметры функций Передача параметров по ссылке позволяет вызываемой функции получить доступ к переменным в вызывающей функции. Использование ссылок при работе со структурами struct free_throws { std::string name; int made; int attempts; float percent; }; void set_pc(free_throws & ft); // использование ссылки на структуру void display(const free_throws & ft); // не разрешать изменения структуры free_throws & accumulate(free_throws & target, const free_throws & source); int main() { free_throws one = {"Ifelsa Branch", 13, 14}; free_throws team = {"Throwgoods", 0, 0}; free_throws dup; / / Инициализация не производится set_pc(one); accumulate(team, one) ; display(accumulate(team, two)); … Зачем возвращать ссылку? double m = sqrt(16.0) ; cout « sqrt (25.0) ; В первом операторе значение 4 .0 копируется во временную ячейку, после чего значение из этой ячейки копируется в m. Во втором операторе значение 5.0 копируется во временную ячейку и затем содержимое этой ячейки передается в cout. dup = accumulate(team,five); Если accumulate() будет возвращать структуру вместо ссылки на структуру, это может повлечь за собой копирование целой структуры во временную ячейку и последующее копирование этой копии в dup. Но благодаря ссылочному возвращаемому значению, team копируется напрямую в dup, что является более эффективным подходом. Выбор объекта, на который указывает возвращаемая ссылка const free_throws & clone2(free_throws & ft) { free_throws newguy; // первый шаг к серьезной ошибке newguy = ft; // копирование информации return newguy; // возврат ссылки на копию } Проще всего избежать такой ошибки за счет возврата ссылки, которая была передана функции в качестве аргумента. Второй метод заключается в использовании операции new для создания нового хранилища. const free_throws & clone (free_throws & ft.) { free_throws * pt; *pt = ft; // копирование информации return *pt; // возврат ссылки на копию } free_throws & jolly = clone(three); Использование ссылок на объект класса #include #include using namespace std; string version1(const string & s1, const string & s2) ; int main() { string input; string copy; string result; cout « "Enter a string: "; getline(cin, input); // ввод строки copy = input; cout << "Your string as entered: " << input « endl; result = version1(input, "***"); // отображение выведенной строки …}string version1(const string & si, const string & s2) { string temp; temp = s2 + s1 + s2; return temp; } Аргументы по умолчанию Аргумент по умолчанию представляет собой значение, которое используется автоматически, если соответствующий фактический параметр в вызове функции не указан. Для этого применяется прототип функции. char * left (const char * str, int n = 1) ; В функции со списком аргументов значения по умолчанию должны добавляться в конце. int harpo(int n, int m = 4, int j = 5) ; // ПРАВИЛЬНО int chico(int n, int m = 6, int j); // НЕПРАВИЛЬНО beeps = harpo (2); //то же, что и harpo (2, 4, 5) beeps = harpo(1,8) ; //тоже, что и harpo(1,8,5) Перегрузка функций В то время как аргументы по умолчанию позволяют вызывать одну и ту же функцию с различным количеством аргументов, полиморфизм функций, также называемый перегрузкой функций, предоставляет возможность использовать несколько функций с одним и тем же именем. С применением перегрузки функций можно разработать семейство функций, которые выполняют в точности одно и то же, но с использованием различных списков аргументов. void print (const char * str, int width); // #1 void print (double d, int width); // #2 void print (long 1, int width); // #3 void print (int i, int width); // #4 void print(const char *str); // #5 print("Pancakes", 15); // используется #1 print("Syrup"); // используется #5 print(1999.0, 10); // используется #2 print(1999, 12); // используется #4 print (1999L, 15); // используется #3 unsigned int year =3210; print(year, 6); // неоднозначный вызов При проверке сигнатур функций компилятор считает, что ссылка на тип и сам тип являются одной и той же сигнатурой. Неверно:double cube(double х) ; double cube(double & x) ; Учитываются различия между переменными с квалификатором const и без него. void dribble(char * bits); // перегружена void dribble (const char *cbits); // перегружена long gronk(int n, float m); // одинаковые сигнатуры, double gronk(int n, float m); // объявления не допускаются long gronk(int n, float m) ; // различные сигнатуры, double gronk(float n, float m) ; // объявления допустимы Шаблоны функций Шаблон функции — это обобщенное описание функции; т.е. он определяет функцию в терминах обобщенного типа, вместо которого может быть подставлен определенный тип данных, такой как int или double. Передавая шаблону тип в качестве параметра, можно заставить компилятор сгенерировать функцию для этого конкретного типа. Поскольку шаблоны позволяют программировать в терминах обобщенного, а не специфического типа, этот процесс иногда называют обобщенным программированием. Поскольку типы представлены параметрами, средство шаблонов иногда называют параметризированными типами. template void Swap(AnyType &a, AnyType &b) { AnyType temp; temp = а; а = b; b = temp; } Первая строка указывает, что устанавливается шаблон, а произвольный тип данных получает имя AnyType. Ключевые слова template и typename являются обязательными; при этом вместо typename можно использовать ключевое слово class. Кроме того, должны присутствовать угловые скобки. Имя типа может быть любым (в этом примере — AnyType), часто используют простые имена, такие как Т. #include template // или class Т // Прототип шаблона функции void Swap(T &а, Т &Ь) ; int main() { using namespace std; int i = 10, j = 20; Swap(i,j); // генерирует void Swap(int &, int &) double x = 24.5, у = 81.7; Swap(x,y); // генерирует void Swap(double &, double &) return 0; } template // или class T // Определение шаблона функции void Swap(T &a, T &b) { T temp; // temp - переменная типа Т temp = a; a = b; b = temp; } Перегруженные шаблоны Можно перегрузить определения шаблонов, точно так же, как перегружаются обычные функции. Как и при перегрузке функций, перегруженные шаблоны должны иметь различные сигнатуры. template // исходный шаблон void Swap(T &a, T &b) ; template // новый шаблон void Swap(T *а, T *b, int n) ; Ограничения шаблонов Предположим, что имеется следующий шаблон функции: template // или template void f (Т а, Т b) {...} Часто в коде делаются предположения относительно того, как операции возможны для того или иного типа. а = b; //не будет верно, если типом Т является встроенный массив if (а > b) //не будет справедливо, если типом Т окажется обычная структура // если это массивы, данная операция сравнивает адреса массивов, а это может быть не тем, что имело в виду.Т с = а*b; //не так в случае, когда Т — массив, указатель или структура: Явные специализации Одно и то же имя может применяться для нешаблонной функции, шаблонной функции и явной специализации шаблона, а также всех перегруженных версий всего перечисленного. Прототип и определение явной специализации должно быть предварено template <>, а также указывать имя обобщенного типа данных. Специализация переопределяет обычный шаблон, а нешаблонная функция переопределяет и специализацию, и шаблон.struct job { char name[4 0] ; double salary; int floor; }; void Swap (job &, job &) ; // Прототип нешаблонной функции template // Прототип шаблона void Swap(T &, T &) ; template <> void Swap (job &, job &) ; // Явная специализация для типа job Компилятор отдает предпочтение нешаблонной версии перед явными специализациями и шаблонными версиями, и предпочитает явную специализацию перед версией, сгенерированной из шаблона. В следующем примере первый вызов функции Swap () использует обобщенный шаблон, а второй вызов — явную специализацию, основанную на типе job: template // шаблон void Swap(T &, T &) ; // Явная специализация для типа job template <> void Swap (job &, job &) ; int main () { double u, v; Swap(u,v); // используется шаблон job a, b; Swap(a,b); // используется void Swap(job &, job &) } Создание экземпляров и специализация Когда компилятор использует шаблон при генерации определения функции для определенного типа данных, результат называется созданием экземпляра шаблона. Шаблон — это не определение функции, но определенный экземпляр шаблона, использующий int, является определением функции. Такой вид создания экземпляров шаблонов называется неявным созданием экземпляров.C++ позволяет выполнять явное создание экземпляров. template void Swap(int, int); // явное создание экземпляра Эквивалент:template <> Swap(int &, int &); // явная специализация template <> Swap(int &, int &) ; // явная специализация Явные создания экземпляров также могут быть обеспечены за счет использования функции в программе. Например: template Т Add(T а, Т b) // передача по значению { return a + b; } …int m = 6; double x = 10.2; cout « Add(x, m) << endl; // явное создание экземпляра Добавление явного создания экземпляров привело к появлению нового синтаксиса префиксов template и template <> в объявлениях, что дает возможность провести различие между явным созданием экземпляров и явной специализацией. Сводка рассмотренных концепций: template void Swap (T &, Т &) ; // прототип шаблона template <> void Swap (job &, job &); // явная специализация для job int main(void) { template void Swap (char &, char &); // явное создание экземпляра для char short a, b; Swap(a,b); // неявное создание экземпляра шаблона для short job n, m;Swap (n, m) ; // использование явной специализации для job char g, h;Swap (g, h) ; // использование явного создания экземпляра шаблона для char } Какую версию функции выбирает компилятор? Рассмотрим пример вызова функции с единственным аргументом: may('В'); // фактический аргумент имеет тип char Прежде всего, компилятор отмечает все кандидаты, каковыми являются функции и шаблоны функций с именем may(). Затем он находит среди них те, которые могут быть вызваны с одним аргументом. Например, в этом случае проверку пройдут следующие функции, поскольку они имеют одно и то же имя и могут использоваться с одним аргументом: void may(int); // #1 float may (float, float = 3); // #2 void may(char); // #3 char * may(const char *) ; // #4 – не подходитchar may (const char &) ; // #5 template void may (const T &) ; // #6 template void may(T *); // #7 – не подходит Далее компилятор должен определить, какая из функций-кандидатов в наибольшей степени соответствует критерию отбора. Он анализирует преобразования, необходимые для того, чтобы аргумент обращения к функции соответствовал аргументу наиболее подходящего кандидата. В общем случае порядок следования от наилучшего к наихудшему варианту можно представить следующим образом. 1. Точное соответствие, при этом обычные функции имеют приоритет перед шаблонами. 2. Преобразование за счет расширения (например, автоматические преобразования char и short в int и float в double). 3. Преобразование с помощью стандартных преобразований (например, преобразование int в char или long в double). 4. Преобразования, определяемые пользователем, такие как те, что определены в объявлениях классов. В некоторых обстоятельствах можно заставить компилятор сделать необходимый вам выбор, правильно написав вызов функции. template // или template Т lesser (Т а, Т b) // #1 { return а < b ? а : b; } int lesser (int a, int b) // #2 { a = a < 0 ? -a : a; b = b < 0 ? -b : b; return a < b ? a : b; } int main() { int m = 20; int n = -30; double x = 15.5; double у = 25.9; cout « lesser(m, n) « endl; // используется #2 cout « lesser(x, y) « endl; // используется #1 с double cout « lesser<>(m, n) « endl; используется #1 с int cout « lesser(x, y) « endl; // используется #1 с int return 0; } Эволюция шаблонных функций template void ft(T1 x, T2 у) { ?тип? хру = х + у; //какой тип использовать в объявлении? } В стандарте С++11 проблема решается с помощью нового ключевого слова decltype. int x; decltype (х) у; // делает тип у тем же, что и у х template void ft(T1 x, T2 у) { decltype (х + у) хру = х + у; } deсltуре(выражение) vаг ; //компилятор должен пройти контрольный список. 1. Если выражение является идентификатором без дополнительных круглых скобок, тогда vаr получит тот же самый тип, что у идентификатора, включая его ква- лификаторы, такие как const: double x = 5.5; double у = 7.9; double &rx = x; const double * pd; decltype(x) w; // w имеет тип double decltype (rx) u = y; // u имеет тип double & decltype(pd) v; // v имеет тип const double * 2. Если выражение является вызовом функции, тогда vаr имеет тип возвращаемого значения этой функции: long indeed(int); decltype (indeed (3)) m; // m имеет тип int 3. Если выражение является lvalue, тогда vаr будет ссылкой на тип выражения. На данной фазе выражение не может быть идентификатором без дополнительных круглых скобок. double хх = 4.4; decltype ( (хх) ) r2 = хх; // г2 имеет тип double & decltype (хх) w = хх; // w имеет тип double (соответствие на фазе 1) 4. Если ни один из предыдущих специальных случаев не применим, тогда ѵаr имеет тот же тип, что и выражение: int j = 3; int &k = j int &n = j ; decltype (j + 6) i1; // i1 имеет тип int decltype(100L) i2; // i2 имеет тип long decltype (k+n) i3; // iЗ имеет тип int; Альтернативный синтаксис для функций (хвостовой возвращаемый тип С++11) template ?тип? gt(T1 x, T2 у) { return x + у; } Прототип double h(int x, float у); может быть записан с помощью альтернативного синтаксиса следующим образом: auto h(int x, float у) -> double; Аналогично:template auto gt(T1 х, Т2 у) -> decltype (х + у) { return х + у; } Модели памяти и пространства имен Раздельная компиляция Язык C++, как и С, позволяет и даже поощряет размещение функций программы в отдельных файлах. При изменении только одного файла можно перекомпилировать лишь этот файл и затем связать его с ранее скомпилированными версиями других файлов. Но нельзя просто вырезать из исходного файла часть кода после окончания функции main(), т.к. функции используют одни и те же объявления структур, поэтому необходимо поместить эти объявления в оба файла. Т.е. разделение программы на несколько файлов создает новые проблемы. Для решения подобных проблем была предоставлена директива #include. В заголовочных файлах обычно содержится следующее: • прототипы функций; • символические константы, определенные с использованием #define или const; • объявления структур; • объявления классов; • объявления шаблонов; • встроенные функции. При включении собственных заголовочных файлов должны использоваться двойные кавычки, а не угловые скобки. Управление заголовочными файлами Заголовочный файл должен включаться в файл только один раз. В C/C++ существует стандартный прием, позволяющий избежать многократных включений заголовочных файлов. Он основан на использовании директивы препроцессора #ifndef (if not defined — если не определено). Показанный ниже фрагмент кода обеспечивает обработку операторов, находящихся между директивами #ifndef и #endif, только в случае, если имя coordin_h_ не было определено ранее с помощью директивы препроцессора #defіnе: #ifndef COORDIN_H_ #define COORDIN_H_ …#endif Пространства имен Декларативная область — это область, в которой могут делаться объявления. Декларативной областью для глобальной переменной является файл. Если объявить переменную внутри функции, ее декларативной областью будет самый внутренний блок, в котором она объявлена. Потенциальная область видимости переменной начинается с точки объявления и простирается до конца ее декларативной области объявления. Таким образом, потенциальная область видимости более ограничена, чем декларативная область, поскольку нельзя использовать переменную в программе ранее позиции, в которой она впервые была определена. Новое средство пространств имен namespace Jack { double pail; // объявление переменной void fetch(); // прототип функции int pal; // объявление переменной struct Well {...}; // объявление структуры } namespace Jill { double bucket(double n) { ... } // определение функции double fetch; // объявление переменной int pal; // объявление переменной struct Hill {...}; // объявление структуры } Пространства имен могут находиться на глобальном уровне или внутри других пространств имен, однако они не могут быть помещены в блок. Пространства имен являются открытыми. Это означает, что можно добавлять новые имена в существующие пространства имен. namespace Jill { char * goose (const char *); } Код функции можно разместить, снова указав пространство имен: namespace Jack { void fetch () {…} } Простейший способ доступа к именам заданного пространства имен предусматривает использование операции разрешения контекста (: :), которая позволяет уточнить имя с помощью его пространства имен: Jack::pail = 12.34; // использование переменной Jill::Hill mole; // создание структуры типа Hill Jack::fetch(); // использование функции Для упрощения использования имен некоторого пространства в C++ предлагаются два механизма — объявление using и директива using. Объявление using предусматривает помещение ключевого слова using перед уточненным именем: using Jill::fetch; // объявление using добавляет имя в локальную декларативную область или в глобальное пространство именДиректива using создается путем предварения идентификатора пространства имен ключевыми словами using namespace. using namespace Jack; // делает доступными все имена в Jack Директивы и объявления using увеличивают вероятность конфликта имен. В случае применения операции разрешения контекста неопределенность не возникает: jack::pal = 3; jill::pal =10; При использовании объявлений using ситуация меняется: using jack::pal; using jill::pal; pal =4; // какая переменная pal имеется в виду? возникает конфликт namespace Jill { double bucket(double n) { ... } double fetch; struct Hill { ... }; }char fetch; // глобальное пространство имен int main () { using namespace Jill; // импорт всех имен из пространства Hill Thrill; // создание структуры типа Jill::Hill double water = bucket (2), // использование функции Jill::bucket(); double fetch; // это не ошибка; имя Jill::fetch скрывается cin » fetch; // чтение значения в локальную переменную fetch cin >> : :fetch; // чтение значения в глобальную переменную fetch cin » Jill::fetch; // чтение значения в переменную Jill::fetch }int foom() {Hill top; // ОШИБКА Jill::Hill crest; // допустимо } Другие возможности пространств имен Объявления пространств имен могут быть вложеннымиnamespace elements { namespace fire { int flame; } float water; } В этом случае ссылка на переменную flame выглядит как elements::fire::flame. Аналогичным образом внутренние имена можно сделать доступными с помощью следующей директивы using: using namespace elements::fire; Можно также пользоваться директивами и объявлениями using внутри пространств имен: namespace myth { using Jill::fetch; using namespace elements; using std::cout; using std::cin; } Предположим, что требуется доступ к переменной Jill: : fetch. Поскольку переменная Jill: : fetch сейчас является частью пространства имен myth, в котором к ней можно обращаться по имени fetch, для доступа к переменной возможен следующий способ: std::cin >> myth::fetch; Новый стиль программирования. • Используйте переменные в именованных пространствах имен вместо внешних глобальных переменных. • Используйте переменные в неименованных пространствах имен вместо статических глобальных переменных. • Если вы разработали библиотеку функций или классов, поместите ее в пространство имен. • Используйте директиву using только в качестве временного средства адаптации устаревшего кода к использованию пространств имен. • Не применяйте директивы using в заголовочных файлах. Прежде всего, этот прием скрывает имена, которые сделаны доступными. Кроме того, порядок следования заголовочных файлов может влиять на поведение программы. Если вы используете директиву using, помещайте ее после всех директив препроцессора #include. • Для импорта имен отдавайте предпочтение операции разрешения контекста или объявлению using. • Для объявлений using отдавайте предпочтение локальной, а не глобальной области видимости. Объекты и классы Объектно-ориентированное программирование (ООП) — это особый концептуальный подход к проектированию программ, и C++ расширяет язык С средствами, облегчающими применение такого подхода. Ниже перечислены наиболее важные характеристики ООП: • абстракция; • инкапсуляция и сокрытие данных; • полиморфизм; • наследование; • повторное использование кода. Класс — это единственное наиболее важное расширение C++, предназначенное для реализации этих средств и связывающее их между собой. Классы в C++ Класс — это двигатель C++, предназначенный для трансляции абстракций в пользовательские типы. Он комбинирует представление данных и методов для манипулирования этими данными в пределах одного пакета. Обычно спецификация класса состоит из двух частей. • Объявление класса, описывающее компоненты данных в терминах членов данных, а также открытый интерфейс в терминах функций-членов, называемых методами. • Определения методов класса, которые описывают, как реализованы определенные функции-члены. Грубо говоря, объявление класса предоставляет общий обзор класса, в то время как определения методов снабжают необходимыми деталями. // stock00.h — интерфейс класса Stock #include class Stock // объявление класса {private: std::string company; long shares; double share_val; double total_val; void set_tot() { total_val = shares * share_val; } public: void acquire (const std:: string & со, long n, double pr) ; void buy (long num, double price); void sell(long num, double price); void update(double price); void show () ; }; // Обратите внимание на точку с запятой в конце. Ключевое слово class в C++ идентифицирует код как определение класса. (typename здесь использовать нельзя.) Данный синтаксис идентифицирует Stock в качестве имени типа для нового класса. Это позволяет объявлять переменные, которые называются объектами или экземплярами типа Stock. Stock sally; Stock solly; Необходимые операции представлены в виде функций-членов (или методов), таких как sell () и update (). Функция-член может быть определена на месте, как, например, settot (), либо представлена с помощью прототипа. Связывание данных и методов в единое целое — наиболее замечательное свойство класса. Управление доступом Метки private и public позволяют управлять доступом к членам класса. Любая программа, которая использует объект определенного класса, может иметь непосредственный доступ к членам из раздела public. Доступ к членам объекта из раздела private программа может получить только через открытые функции-члены из раздела public (или же через дружественные функции). Эта изоляция данных от прямого доступа со стороны программы называется сокрытием данных. Проектное решение класса пытается отделить открытый интерфейс от специфики реализации. Открытый интерфейс представляет абстрактный компонент проектного решения. Собрание деталей реализации в одном месте и отделение их от абстракции называется инкапсуляцией. Сокрытие данных (помещение данных в раздел private класса) является примером инкапсуляции, и поэтому оно скрывает функциональные детали реализации в разделе private, как это сделано в классе Stock с функцией set_tot (). Другим примером инкапсуляции может служить обычная практика помещения определений функций класса в файл, отдельный от объявления класса. public или private? Одним из главных принципов ООП является сокрытие данных, т.е. единицы данных обычно размещаются в разделе private. Функции-члены, которые образуют интерфейс класса, размещаются в разделе public; в противном случае вызвать эти функции из программы не удастся. Как правило, закрытые функции-члены применяются для управления деталями реализации, которые не формируют часть открытого интерфейса. Использовать ключевое слово private в объявлении класса не обязательно, поскольку это спецификатор доступа к объектам класса по умолчанию: class World { float mass; // по умолчанию private char name [20]; // по умолчанию private public: vоid tellall(void); … } Реализация функций-членов класса Определения функций-членов очень похожи на определения обычных функций. Каждое из них имеет заголовок и тело. Определения функций-членов могут иметь тип возврата и аргументы. Но, кроме того, с ними связаны две специфических характеристики. • При определении функции-члена для идентификации класса, которому принадлежит функция, используется операция разрешения контекста (: :). • Методы класса имеют доступ к private-компонентам класса. Операция разрешения контекста идентифицирует класс, к которому данный метод относится. void Stock::update(double price) Говорят, что идентификатор update() имеет область видимости класса. Другие функции-члены класса Stock могут при необходимости использовать метод update() без операции разрешения контекста. Это связано с тем, что они принадлежат одному классу, и, следовательно, имеют общую область видимости. Stock::update () называется уточненным именем функции. Встроенные методы Любая функция с определением внутри объявления класса автоматически становится встроенной. Это значит, что Stock::settot() является встроенной функцией.Альтернатива:class Stock { private: void set_tot(); // определение оставлено отдельным public: }; inline void Stock::set_tot() // использование inline в определении { total_val = shares * share_val; } Какой объект использует метод? Stock kate, joe; kate.show(); // объект kate вызывает функцию-член joe.show(); // объект joe вызывает функцию-член Каждый вновь созданный объект содержит хранилище для собственных внутренних переменных-членов класса, однако все объекты одного класса разделяют общий набор методов, по одной копии каждого. Вызов функции-члена называется отправкой сообщения. Отправка сообщения двум разным объектам вызывает один и тот же метод, который применяется к двум разным объектам Использование классов #include #include "stock00.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; } Клиент-серверная модель Согласно этой концепции, клиентом является программа, которая использует класс. Объявление класса, включая его методы, образует сервер, который является ресурсом, доступным нуждающейся в нем программе. Клиент взаимодействует с сервером только через открытый (public) интерфейс. Это означает, что единственной ответственностью клиента и, как следствие — программиста, является знание интерфейса. Ответственностью сервера и, как следствие — его разработчика, является обеспечение того, чтобы его реализация надежно и точно соответствовала интерфейсу. Любые изменения, вносимые разработчиком сервера в класс, должны касаться деталей реализации, но не интерфейса. Конструкторы и деструкторы классов Одна из целей C++ состоит в том, чтобы сделать использование объектов классов подобным применению стандартных типов. Однако к данным класса разрешен только закрытый доступ, а это означает, что единственный способ, с помощью которого программа может получить доступ к ним — через функции-члены. Следовательно, для успешной инициализации объекта понадобится придумать соответствующую функцию-член. Для автоматической инициализации используются специальные функции-члены, называемые конструкторами класса, которые предназначены для создания новых объектов и присваивания значений их членам-данным. Объявление и определение конструкторов // Прототип конструктора с несколькими аргументами по умолчанию Stock (const string & со, long n = 0, double pr = 0.0) ; // Определение конструктора Stock::Stock(const string & со, long n, double pr) { company = со; if (n < 0) { std::cerr << "Number of shares can't be negative; " << company « " shares set to 0.\n"; shares = 0; } else shares = n; share_val = pr; set_tot () ; } Использование конструкторов Язык C++ предлагает два способа инициализации объектов с помощью конструктора. Первый — вызвать конструктор явно: Stock food = Stock("World Cabbage", 250, 1.25); Это устанавливает значение члена company объекта food равным строке "World Cabbage", значение shares равным 250 и т.д. Второй способ — вызвать конструктор неявно: Stock garment("Furry Mason", 50, 2.5); Эта более компактная форма эквивалентна следующему явному вызову: Stock garment = Stock("Furry Mason", 50, 2.5); Конструкторы по умолчанию Конструктор по умолчанию — это конструктор, который используется для создания объекта, когда не предоставлены явные инициализирующие значения. Stock fluffy_the_cat; // используется конструктор по умолчанию Для класса Stock конструктор по умолчанию будет таким: Stock::Stock() { } После того, как вы определите хотя бы один конструктор класса, компилятор перестанет создавать конструктор по умолчанию и нужно создавать свой. Деструкторы В случае использования конструктора для создания объекта программа отслеживает этот объект до момента его исчезновения. В этот момент программа автоматически вызывает специальную функцию-член с названием деструктор. Деструктор имеет специальное имя, формируемое из имени класса и предваряющего его символа тильды (~). ~Stock (); Подобно конструктору, деструктор не имеет ни возвращаемого значения, ни объявляемого типа. Однако в отличие от конструктора, деструктор не должен иметь аргументы. Обычно деструктор не должен явно вызываться в коде. #include class Stock { private: std::string company; long shares; double share_val; double total_val; void set_tot() { total_val = shares * share_val; } public: // Два конструктора Stock(); // конструктор по умолчанию Stock(const std::string & со, long n = 0, double pr = 0.0); ~Stock(); // деструктор void buy (long num, double price); void sell(long num, double price); void update(double price); void show () ; }; Списковая инициализация С++11 Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0}; Stock jock {"Sport Age Storage, Inc"}; Stock temp {}; Функции-члены const const Stock land = Stock("Kludgehorn Properties"); land.show(); void show() const; // обещает не изменять вызываемый объект Указатель this Каждая функция-член, включая конструкторы и деструкторы, имеет указатель this. Специфическим свойством this является то, что он указывает на вызывающий объект. Если метод нуждается в получении ссылки на вызвавший объект в целом, он может использовать и выражение *this. Применение квалификатора const после скобок с аргументами заставляет трактовать this как указатель на const, в этом случае вы не можете использовать this для изменения значений объекта. Массив объектов Stock mystuff[4]; // создание массива из 4 объектов Stock Такое объявление требует либо отсутствия у класса явно определенных конструкторов (при этом используются неявные, ничего не делающие конструкторы), либо, как и в представленном случае — чтобы был явно определен конструктор по умолчанию. mystuff [0] .update(); // применяет update() к первому элементу mystuff[3].show(); // применяет show() к 4-му элементу Для инициализации элементов массива можно использовать конструктор. В этом случае необходимо вызывать конструктор для каждого индивидуального элемента: const int STKS = 4; Stock stocks[STKS] = { Stock("NanoSmart", 12.5, 20), Stock() , Stock("Monolithic Obelisks", 130, 3.25) };

Приложенные файлы

  • ppt 14703479
    Размер файла: 3 MB Загрузок: 0

Добавить комментарий