Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Лекция 2.

·  Историческая справка

·  Основные принципы объектно-ориентированного программирования (ООП)

·  Преимущества и недостатки С++.

·  Комментарии.

·  Константы.

·  Встраиваемые функции.

·  Объявления структур, смесей и перечислений.

·  Объявления переменных.

·  Ссылки.

·  Использование void.

Историческая справка

Язык С++ был создан в начале 80-х годов в Bell Labs Бьерном Страуструпом для целей написания имитационных программ. В то время единственным подходящим для этого объектно-ориентированным языком был язык Симула-67, который можно было бы считать идеальным средством, если бы не его очень низкая эффективность.

Страуструп решил создать новую версию языка Си, которая содержала бы элементы подобные элементам языка Симула-67, позволяющие писать объектно-ориентированные программы, при этом сохраняя простоту выражений и скорость выполнения, присущие языку Си. Таким образом, основой для нового языка стал Си, а прообразами также послужили Симула-67 и Алгол-68. Из Симулы-67 (основного источника вдохновения) были взяты концепции классов с возможностью их порождения друг из друга и виртуальными функциями. Из Алгола-68 были заимствованы возможности перегрузки операций и свобода помещения переменной в любое место программы (вперемешку с операторами). Язык Си был выбран в качестве базового по нескольким причинам:

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

НЕ нашли? Не то? Что вы ищете?

-эффективность, т. к. семантика языка отражает архитектуру традиционного компьютера;

-доступность: в любой системе для любого компьютера наверняка найдется приемлемый компилятор с языка Си;

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

C++ - универсальный язык программирования. За исключением второстепенных деталей C++ является надмножеством языка программирования Cи. Помимо возможностей, которые дает Cи, C++ предоставляет гибкие и эффективные средства определения новых типов. Используя определения новых типов, точно отвечающих концепциям приложения, программист может разделять разрабатываемую программу на легко поддающиеся контролю части. Такой метод построения программ часто называют абстракцией данных. Информация о типах содержится в некоторых объектах типов, определенных пользователем. Такие объекты просты и надежны в использовании в тех ситуациях, когда их тип нельзя установить на стадии компиляции. Программирование с применением таких объектов часто называют объектно-ориентированным. При правильном использовании этот метод дает более короткие, проще понимаемые и легче контролируемые программы.

Ключевым понятием C++ является класс. Класс - это тип, определяемый пользователем. Классы обеспечивают скрытие данных, гарантированную инициализацию данных, неявное преобразование типов для типов, определенных пользователем, динамическое задание типа, контролируемое пользователем управление памятью и механизмы перегрузки операций. C++ предоставляет гораздо лучшие, чем в Cи, средства выражения модульности программы и проверки типов. В языке есть также усовершенствования, не связанные непосредственно с классами, включающие в себя символические константы, inline - подстановку функций, параметры функции по умолчанию, перегруженные имена функций, операции управления свободной памятью и ссылочный тип. В C++ сохранены возможности языка Cи по работе с основными объектами аппаратного обеспечения (биты, байты, слова, адреса и т. п.). Это позволяет весьма эффективно реализовывать типы, определяемые пользователем.

C++ и его стандартные библиотеки спроектированы так, чтобы обеспечивать переносимость. Имеющаяся на текущий момент реализация языка будет идти в большинстве систем, поддерживающих Cи. Из C++ программ можно использовать Cи библиотеки, и с C++ можно использовать большую часть инструментальных средств, поддерживающих программирование на C.

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

понятиям той прикладной области, для которой разрабатывается программа, чем встроенные типы данных, ориентированные на архитектуру ЭВМ.

Основные принципы объектно-ориентированного программирования (ООП)

Объектно-ориентированное программирование (ООП) сегодня весьма актуально. Для многих сам язык программирования С++ является синонимом ООП - точно также, как и программирование на LISP ассоциируется с искуственным интеллектом. В действительности ООП является не столько продолжением того или иного языка программирования, сколько результатом применения особых методов. Приложения можно разрабатывать с помощью ООП на таких языках, как Pascal, Ada, Basic и ассемблер, хотя и с определёнными трудностями.

В 80-х годах язык Си стал одним из самых распространённых и универсальных языков программированияю. С помощью этого языка можно было создавать переносимый код для широкого класса компьютеров. В результате строки разработок программного обеспечения сократились, а объёмы проектов увеличились. Но с увеличением объёмов повышалась и сложность разработок, что и привело к удлиннению сроков. Сегодня сокращение времени и усилий для разработки программного обеспечения стало главной задачей многих компаний и организаций. Пытаясь использовать примущества ООП в среде Си и сохранить при этом многие желанные возможности, в частности простоту и быстродействие, компания AT&T разработала язык С++ как расширение ANSI Си.

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

Несколько лет назад исследователи заметили, что программисты в течение определённого времени пишут и отлаживают приблизительно одинаковый по объёму код, независимо от используемого языка программирования. Объём работы приблизительно тот же, но результаты разные. Написание 100 строк кода на Си требует столько же затрат, сколько и 100 строк кода на ассемблере, но возможности кода на Си гораздо шире. Поняв это, исследователи начали разрабатывать языки высокого урорвня, которые увеличивали бы потенциал отдельного программиста, сокращая тем самым сроки и стоимость разработки проектов.

В 70-х годах среди создателей языков программирования приобрела популярность концепция объекта. Объект является совокупностью кода и данных, созданной для воспроизведения свойств физических предметов или абстрактных понятий. Объекты эффективны как элементы программирования, если они представляют собой прямую абстракцию обычно используемых предметов и в значительной степени скрывают сложность своей реализации от пользователей. Первыми были разработаны объекты, тесно связанные с компьютерами, такие как Integer, Array и Stack. В некоторых языках (например Smalltalk) всё представлено в виде объектов.

Объектно-ориентированная программирование - это методология, которая которая концентрируется больше на связях между объектами, чем на деталях реализации. Данные связи обычно развиваются по принципу деревьев, при этом новые типы объектов образуются из уже существующих. Сокрытие реализации объекта ведёт к тому, что пользователя больше волнует связь с остальными объектами, чем поведение объекта. Данное отличие важно, поскольку оно означает принципиальный отказ от "процедурных" языков (таких как Си), в которых функции и вызовы функций составляют основу деятельности.

В С++ некоторые объекты являются частью самого языка. Но в целом задача создания новых объектов ложится на пользователя. Эффективность ООП проявляется только при создании и применении групп связанных между собой объектов. Такие группы называют иерархиями классов. Развитие этих иерархий классов является основой деятельности в ООП.

Преимущества и недостатки С++.

Часто говорят, что программирование на С++ гарантирует создание хороших программ. Вот некоторые из известных преимуществ С++:

q  Новые программы разрабатываются в более короткие сроки из-за повторного использования старого кода

q  Создание и использование новых типов данных - легче, чем в Си

q  Управление памятью в С++ много легче и яснее

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

q  "Сокрытие данных", т. е. пока используются данные одной частью программы, они недоступны её другой части, легче реализуемо в С++

Какие из этих заявлений верны? Вероятно, что С++ несколько переоценивают, В основном всё вышесказанное верно для объектно-ориентированного стиля программирования. Ажиотаж вокруг С++ имеет сходство с такими же формальными заявлениями относительно языка искусственного интеллекта как LISP и Prolog: эти языки предполагалось использовать для решения "почти без проблем" особо трудных проблем искусственного интеллекта. Очевидно, что такой ажиотаж вокруг любого языка программирования является явным переусердствованием: В конце концов любую проблему можно решить программно на любом языке (даже на Бэйсике или Прологе). Преимущества или недостатки конкретного языка программирования заключаются в том, "что вы можете делать с ними", а лучше "какой инструментальный язык позволяет сделать работу легче".

Что касается приведённых заявлений относительно С++, то можно сделать следующее заключение. Разработка новых программ с многократным использованием существующего кода возможна и с использованием языка Си, например, с использованием библиотек функций: в результате обрабатывающие функции можно собрать в библиотеку и их не нужно изобретать заново в каждой новой программе. А С++ позволяет сделать это с помощью возможностей особого синтаксиса для многократного использования кода, дополнительно к возможностям библиотеки функций.

Создание и использование новых типов данных также очень даже и возможно в Си; например, с помощью структур, typedefs и т. д.. Из этих типов можно породить другие типы, что ведёт к структурам, включающим другие структуры и т. д.

Управление распределением памяти в принципе в С++ также легко или также трудно, как и в Си. В особенности, когда используются такие Си-шные функции как xmalloc() и xrealloc(). Эти функции часто используются в наших Си программах, они выделяют память или прерывают работу программы, когда исчерпываются резервы памяти. Короче, управление памятью в Си или в С++ можно представить "элегантно", "безобразно" или что-то среднее - это зависит больше от разработчика, чем от самого языка.

Относительно "склонности к дефектам" можно сказать, что С++ действительно использует более строгую проверку типов чем в Си. Однако, большинство современных Си компиляторов реализует "предупреждающие уровни"; это когда программист выбирает учитывать или нет сгенерированное предупреждающее сообщение. В С++ многие из таких предупреждений становятся фатальными ошибками (остановка компилятора).

Ну, и наконец что касается "сокрытии данных", то Си располагает таким же инструментом. Например, там, где это возможно, можно использовать местные (local) или статические переменные и специальные типы данных такие, как структуры, которыми можно манипулировать только посвящённым для этого функциям. Использую подобную технику, "сокрытие данных" можно реализовать даже в Си; правда нужно заметить, что С++ позволяет сделать это за счёт специальных синтаксических конструкций. И напротив, программисты, которые предпочитают использовать глобальную переменную int i для каждой счётной переменной, будут похоже не очень довольны концепцией "сокрытия данных", будь это в Си или в С++.

Подводя черту, можно сказать, что С++ в частности и ООП в особенности не представляют собой решений всех проблем, связанных с программированием. С++, однако, предоставляет такие изящные синтаксические возможности, которые привлекают к себе пристальное внимание разработчиков всего мира. В то же время, уровень грамматической сложности С++ сильно вырос по сравнению с Си. Со временем мы привыкли к возросшей сложности, но такой переход не прошёл быстро и безболезненно.

ОБЗОР НОВЫХ ВОЗМОЖНОСТЕЙ С++

С++ КАК УЛУЧШЕНИЕ СИ. Новые возможности, которые одинаковы для большинства реализации С++.

1. Комментарии. Распространенной ошибкой при написании Си-программ являются незакрытые комментарии. Ее не всегда легко найти, т. к. компилятор не может ее однозначно идентифицировать. Для избежания этих ошибок в С++ введен новый символ комментариев - //, действие которого распространяется на текущую строку.

В соответствии с определением ANSI, "комментарий до конца строки" реализуется в синтаксисе С++. Этот комментарий начинается с символов // и заканчивается маркером конца строки. Всё ещё можно использовать стандартный Си комментарий, определённый с помощью /* и */.

int main()

{

// this is end-of-line comment

// one comment per line

/* this is standard-C comment, over more

than one line */

return (0);

}

В некоторых Си компиляторах, таких как Microsoft C Compiler V5, построчный комментарий был реализован как расширение к Си.

2. Константы. В языке Си символические константы в основном определялись с помощью директивы препроцессора #define, вместо этого в С++ рекомендуется использовать объявление переменной с начальным значением и ключевым словом  const

const int count = 100;

Такая переменная не может быть программно изменена и может быть использована везде, где требуется константа, например для обозначения размерности при объявлении массива. С помощью ключевого слова  const можно объявить указатель на константу, чего нельзя сделать через директиву #define:

int temp;  // переменная

сonst int count = 100;  // константа

const int *pcount = &count;  // указатель на константу

int * const ptemp = &temp;  // константный указатель на переменную

const int * const pc = &count;  // константный указатель на константу

При попытке изменить константу или константный указатель компилятор выдает сообщение об ошибке: "Cannot modify a const object" - не могу модифицировать константный объект.

3. Встраиваемые функции. В программе на Си директива #define использовалась также для создания макроопределений. Макроопределения играют важную роль в программе на Си, но их некорректное определение часто приводит к труднообнаружимым ошибкам. Классической ошибкой такого рода являются опущенные круглые скобки в макроподстановке.

В С++ для определения функции, которая должна встраиваться как макрорасширение (встраиваемой функции), используется ключевое слово inline. Таким образом, вместо #define SUMMA(a, b) ((a)+(b)), как принято в языке Си, в С++ предпочтительнее записать:

inline double SUMMA(double a, double b){ return (a + b); }

При определении и использовании встраиваемой функции необходимо помнить, что:

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

- имеет смысл определять как встраиваемые только очень маленькие функции, т. к. большие функции значительно удлинняют объектный код и увеличивают время компиляции, хотя при этом уменьшается время выполнения;

- ключевое слово inline является лишь рекомендацией компилятору, что данную функцию необходимо сделать встраиваемой. Компилятор сам решает, сделать ее встраиваемой или нет. При этом он руководствуется:

а) размером функции (в некоторых версиях до 1200 строк);

в) если функция является рекурсивной, то встраиваемым может быть лишь первый вызов;

с) в некоторых реализациях в теле встраиваемой функции нельзя использовать операторы цикла for, while, do, break, continue. В этом случае выдается предупреждение: "Functions containing for are not expanded inline".

В результате, использование ключевого слова inline для определения встраиваемых функций и ключевого слова const для объявления символических констант позволяет полностью исключить директиву препроцессора #define из употребления.

4. Объявления структур, смесей и перечислений. В С++, в отличие от Си, имена типов структур, перечислений и смесей рассматриваются как полноценные типы, определенные пользователем. Таким

образом, при объявлении переменных данного типа не требуется указывать ключевые слова struct, enum и union. Например, фрагмент Си-программы

enum day {sun, mon, tue, wen, ... };

struct path

{

char str[40];

enum day week;

};

struct path list;

на языке С++ примет вид:

enum day {sun, mon, tue, wen, ... };

struct path

{

char str[40];

day week;

};

path list;

В С++ также введена возможность использования неименованных перечислений и смесей (но не структур!). Использование неименованных перечислений является альтернативной формой определения символических констант, например,

enum { FALSE, TRUE };

определяет две символические константы: FALSE=0 и TRUE=1. Обе константы определяют тип int и могут быть использованы везде, где возможно использование константы. Однако к ней нельзя применить операцию взятия адреса &, т. к. под нее не отводится память во время выполнения.

Непоименованные смеси удобно использовать для определения переменной части структур. При этом упрощается ее описание и сокращается один уровень уточнения при обращении к полю смеси.

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

struct Sklad { ... static int count; ... };

Sklad sk1, sk2;

В переменной count можно хранить информацию, общую для всех объектов данного типа. Доступ к ней возможен двумя способами:

- через имя объекта sk. count;

- используя имя структуры и операцию области видимости: Sklad::count.

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

int Sklad :: count = 5;

5. Определение локальных переменных

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

Кроме того, локальные переменные можно определять и в самих выражениях. Типичным примером может служить следующее выражение:

#include <stdio. h>

int main()

{

for (register int i = 0; i < 20; i++) printf("%d\n", i);

return (0);

}

В этом фрагменте переменная i создаётся внутри выражения. В соответствии с ANSI стандартом, переменной не существует ни до выражения for, ни за ним. Для некоторых компиляторов, переменная продолжает существовать после выполнения оператора for, но со следующим предупреждением:

предупреждение: при поиске имя 'i', заменено для нового ANSI `for' в области использования устаревшей связи для `i',

которое будет сделано, когда переменная используется за пределами цикла for. Всё становится на свои места, если определить переменную перед циклом for, если она продолжает использоваться и за пределами цикла, в противном случае она будет использоваться только внутри самого цикла.

Определение локальных переменных, когда это необходимо, требует небольшой привычки. Однако, обычно предпочитают иметь более читабельный код, чем опеределение переменных в начале составных выражений. При определении локальных переменных мы предлагаем следующие правила большого пальца:

"Локальные переменные должны быть определены в самом начале функции, сразу же за первой фигурной скобкой или их нужно создавать как можно ближе к месту их использования" так, как это было показано в предыдущем примере. Это касается не только цикла for, а также всех ситуаций, где только необходимы переменные и в любом месте функции.

6. Ссылки. В С++ введен новый тип данных - ссылка. Ссылка позволяет определить альтернативное имя переменной:тип &идентификатор1 = идентификатор2;

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

int a, b;

int &alt = a; // alt является ссылкой на a

alt = b; // a = b;

alt++; // a++;

Объявление ссылки напоминает объявление указателя, только вместо * используется &. Пусть имеется объявление

int *point = &a;

Тогда следующие условные выражения всегда будут истинны:*point == alt, point == &alt.

Ссылку можно рассматривать как постоянный указатель, который всегда разадресован, и поэтому для него не надо выполнять операцию разадресации (т. е. * - получения значения по указателю).

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

&alt == &a как правило, будет истинным. Из этого правила есть два исключения:

а) При объявлении ссылки на константу. В этом случае генерируется внутренняя рабочая переменная, ей присваивается данная константа, а затем ссылка инициализируется этой переменной:

char &refchar = '\0'; -> char temp = '\0';

char &refchar = temp;

Это делается из соображений безопасности. Многие компиляторы производят оптимизацию памяти и хранят одинаковые константы только в одном экземпляре. В этом случае есть возможность через ссылку испортить константу.

в) При инициализации ссылки переменной другого типа:

unsigned int ui = 20; -> int temp2 = (int) ui;

int &refi = ui; int &refi = temp2;

В этом случае компилятор добавляет явное преобразование типа.

В тех случаях, когда при объявлениях ссылки создается копия объекта, компилятор выдает предупреждение:

Temporary used to initialize 'variable_name' 0.

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

7. Использование void. 0 Как и в стандартном Си ключевое слово void может использоваться для указания того, что функция не возвращает значения и не принимает параметров:

void funname(void);

В более поздних реализациях Си были введены еще два способа использования этого слова, сохраненные в С++.

Ключевое слово void может быть использовано для указания компилятору, что значение вычисляемого выражения игнорируется, например:

int foo(int i){ return i*i; }

main()

{

int k = 5;

(void) foo (k); // Возвращаемое значение

return 0; // игнорируется

}

Практически это используется редко, т. к. большинство компиляторов (в том числе и  Borland C++ 4.0) выполняют то же самое по умолчанию. Наиболее интересно и практически полезно использование ключевого слова void для объявления указателя на неопределенный тип. Такому указателю может быть присвоен указатель на любой тип, но не наоборот. Для того, чтобы присвоить указатель на void какому-либо другому указателю, должна быть выполнена операция явного приведения типа:

void *vp;

int i;

double cosinus[3] = { 0.1, 0.2, 0.35 };

int *pi = &i;

double *pd = cosinus;

vp = pi;

pd = (double *) vp;

По указателю void нельзя получить значение без явного приведения типа (понятно!). Перед использованием такого указателя в адресной арифметике он также должен быть приведен к нужному типу:

vp = pi; k += *((int *) vp);

vp = cosinus; ((double *) vp)++;

Основное использование указателей на void заключается в описании формальных параметров функции:

void memcpy(void *dest, const void *source, int n);

В дополнение к правилам преобразования типов в С++ добавлены следующие неявные преобразования указателей:

- константа 0 корректно преобразуется к нулевому указателю (NULL), независимо от модели памяти;

- указатель на любой тип преобразуется к указателю на void.