C++
C++ читать книгу онлайн
С++ – это универсальный язык программирования, задуманный так, чтобы сделать программирование более приятным для серьезного программиста. За исключением второстепенных деталей С++ является надмножеством языка программирования C. Помимо возможностей, которые дает C, С++ предоставляет гибкие и эффективные средства определения новых типов. Используя определения новых типов, точно отвечающих концепциям приложения, программист может разделять разрабатываемую программу на легко поддающиеся контролю части. Такой метод построения программ часто называют абстракцией данных. Информация о типах содержится в некоторых объектах типов, определенных пользователем. Такие объекты просты и надежны в использовании в тех ситуациях, когда их тип нельзя установить на стадии компиляции. Программирование с применением таких объектов часто называют объектно-ориентированным. При правильном использовании этот метод дает более короткие, проще понимаемые и легче контролируемые программы.
Ключевым понятием С++ является класс. Класс – это тип, определяемый пользователем. Классы обеспечивают сокрытие данных, гарантированную инициализацию данных, неявное преобразование типов для типов, определенных пользователем, динамическое задание типа, контролируемое пользователем управление памятью и механизмы перегрузки операций. С++ предоставляет гораздо лучшие, чем в C, средства выражения модульности программы и проверки типов. В языке есть также усовершенствования, не связанные непосредственно с классами, включающие в себя символические константы, inline-подстановку функций, параметры функции по умолчанию, перегруженные имена функций, операции управления свободной памятью и ссылочный тип. В С++ сохранены возможности языка C по работе с основными объектами аппаратного обеспечения (биты, байты, слова, адреса и т.п.). Это позволяет весьма эффективно реализовывать типы, определяемые пользователем.
С++ и его стандартные библиотеки спроектированы так, чтобы обеспечивать переносимость. Имеющаяся на текущий момент реализация языка будет идти в большинстве систем, поддерживающих C. Из С++ программ можно использовать C библиотеки, и с С++ можно использовать большую часть инструментальных средств, поддерживающих программирование на C.
Эта книга предназначена главным образом для того, чтобы помочь серьезным программистам изучить язык и применять его в нетривиальных проектах. В ней дано полное описание С++, много примеров и еще больше фрагментов программ.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
// dc.h: общие описания для калькулятора
enum token_value (* NAME, NUMBER, END, PLUS='+', MINUS='-', MUL='*', DIV='/', PRINT=';', ASSIGN='=', LP='(', RP=')' *);
extern int no_of_errors; extern double error(char* s); extern token_value get_token(); extern token_value curr_tok; extern double number_value; extern char name_string[256];
extern double expr(); extern double term(); extern double prim();
struct name (* char* string; name* next; double value; *);
extern name* look(char* p, int ins = 0); inline name* insert(char* s) (* return look(s,1); *)
Если опустить фактический код, то lex.c будет выглядеть примерно так:
// lex.c: ввод и лексический анализ #include «dc.h»
#include «ctype.h»
token_value curr_tok; double number_value; char name_string[256];
token_value get_token() (* /* ... */ *)
Заметьте, что такое использование заголовочных файлов гарантирует, что каждое описание в заголовочном файле объета, определенного пользователем, будет в какой-то момент включено в файл, где он определяется. Например, при компилции lex.c компилятору будет передано:
extern token_value get_token(); // ... token_value get_token() (* /* ... */ *)
Это обеспечивает то, что компилятор обнаружит любую нсогласованность в типах, указанных для имени. Например, если бы get_token() была описана как возвращающая token_value, но при этом определена как возвращающая int, компиляция lex.c не прошла бы изза ошибки несоответствия типов.
Файл syn.c будет выглядеть примерно так:
// syn.c: синтаксический анализ и вычисление
#include «dc.h»
double prim() (* /* ... */ *) double term() (* /* ... */ *) double expr() (* /* ... */ *)
Файл table.c будет выглядеть примерно так:
// table.c: таблица имен и просмотр
#include «dc.h»
extern char* strcmp(const char*, const char*); extern char* strcpy(char*, const char*); extern int strlen(const char*);
const TBLSZ = 23; name* table[TBLSZ];
name* look(char* p; int ins) (* /* ... */ *)
Заметьте, что table.c сам описывает стандартные функции для работы со строками, поэтому никакой проверки согласованости этих описаний нет. Почти всегда лучше включать заголвочный файл, чем описывать имя в .c файле как extern. При этом может включаться «слишком много», но это обычно не окзывает серьезного влияния на время, необходимое для компилции, и как правило экономит время программиста. В качестве примера этого, обратите внимание на то, как strlen() заново описывается в main() (ниже). Это лишние нажатия клавиш и воможный источник неприятностей, поскольку компилятор не может проверить согласованность этих двух определений. На самом дле, этой сложности можно было бы избежать, будь все описания extern помещены в dc.h, как и предлагалось сделать. Эта «нережность» сохранена в программе, поскольку это очень типично для C программ, очень соблазнительно для программиста, и чаще приводит, чем не приводит, к ошибкам, которые трудно обнаржить, и к программам, с которыми тяжело работать. Вас предуредили!
И main.c, наконец, выглядит так:
// main.c: инициализация, главный цикл и обработка ошибок
#include «dc.h»
int no_of_errors;
double error(char* s) (* /* ... */ *)
extern int strlen(const char*);
main(int argc, char* argv[]) (* /* ... */ *)
Важный случай, когда размер заголовочных файлов станвится серьезной помехой. Набор заголовочных файлов и библиотеку можно использовать для расширения языка множеством общи специальноприкладных типов (см. Главы 5-8). В таких случаях не принято осуществлять чтение тысяч строк заголовоных файлов в начале каждой компиляции. Содержание этих файлов обычно «заморожено» и изменяется очень нечасто. Наиболее плезным может оказаться метод затравки компилятора содержанием этих заголовочных фалов. По сути, создается язык специального назначения со своим собственным компилятором. Никакого стадартного метода создания такого компилятора с затравкой не принято.
4.3.2 Множественные заголовочные файлы
Стиль разбиения программы с одним заголовочным файлом наиболее пригоден в тех случаях, когда программа невелика и ее части не предполагается использовать отдельно. Поэтому то, что невозможно установить, какие описания зачем помещены в заголовочный файл, несущественно. Помочь могут комментарии. Другой способ – сделать так, чтобы каждая часть программы имела свой заголовочный файл, в котором определяются предотавляемые этой частью средства. Тогда каждый .c файл имеет соответствующий .h файл, и каждый .c файл включает свой собтвенный (специфицирующий то, что в нем задается) .h файл и, возможно, некоторые другие .h файлы (специфицирующие то, что ему нужно).
Рассматривая организацию калькулятора, мы замечаем, что error() используется почти каждой функцией программы, а сама использует только «stream.h». Это обычная для функции ошибок ситуация, поэтому error() следует отделить от main():
// error.h: обработка ошибок
extern int no_errors;
extern double error(char* s);
// error.c
#include «stream.h» #include «error.h»
int no_of_errors;
double error(char* s) (* /* ... */ *)
При таком стиле использования заголовочных файлов .h файл и связанный с ним .c файл можно рассматривать как мдуль, в котором .h файл задает интерфейс, а .c файл задает реализацию. Таблица символов не зависит от остальной части калькулятора за исключением использования функции ошибок. Это можно сделать явным:
// table.h: описания таблицы имен
struct name (* char* string; name* next; double value; *);
extern name* look(char* p, int ins = 0); inline name* insert(char* s) (* return look(s,1); *)
// table.c: определения таблицы имен
#include «error.h» #include «string.h» #include «table.h»
const TBLSZ = 23; name* table[TBLSZ];
name* look(char* p; int ins) (* /* ... */ *)
Заметьте, что описания функций работы со строками теперь включаются из «string.h». Это исключает еще один возможный источник ошибок.
// lex.h: описания для ввода и лексического анализа
enum token_value (* NAME, NUMBER, END, PLUS='+', MINUS='-', MUL='*', DIV='/', PRINT=';', ASSIGN='=', LP='(', RP=')' *);
extern token_value curr_tok; extern double number_value; extern char name_string[256];
extern token_value get_token();
Этот интерфейс лексического анализатора достаточно бепорядочен. Недостаток в надлежащем типе лексемы обнаруживает себя в необходимости давать пользователю get_token() фактческие лексические буферы number_value и name_string.
// lex.c: определения для ввода и лексического анализа
#include «stream.h» #include «ctype.h» #include «error.h» #include «lex.h»
token_value curr_tok; double number_value; char name_string[256];
token_value get_token() (* /* ... */ *)
Интерфейс синтаксического анализатора совершенно прозрчен: // syn.c: описания для синтаксического анализа и вычисления
extern double expr(); extern double term();
extern double prim();
// syn.c: определения для синтаксического анализа и // вычисления
#include «error.h» #include «lex.h» #include «syn.h»
double prim() (* /* ... */ *) double term() (* /* ... */ *) double expr() (* /* ... */ *)
Главная программа, как всегда, тривиальна:
// main.c: главная программа
#include «stream.h» #include «error.h» #include «lex.h» #include «syn.h» #include «table.h» #include «string.h»
main(int argc, char* argv[]) (* /* ... */ *)
Сколько заголовочных файлов использовать в программе, зависит от многих факторов. Многие из этих факторов сильнее связаны с тем, как ваша система работает с заголовочными фалами, нежели с С++. Например, если в вашем редакторе нет средств, позволяющих одновременно видеть несколько файлов, использование большого числа файлов становится менее привлкательным. Аналогично, если открывание и чтение 10 файлов по 50 строк в каждом требует заметно больше времени, чем чтение одного файла в 500 строк, вы можете дважды подумать, прежде чем использовать в небольшом проекте стиль множественных зголовочных файлов. Слово предостережения: набор из десяти зголовочных файлов плюс стандартные заголовочные файлы обычно легче поддаются управлению. С другой стороны, если вы разбили описания в большой программе на логически минимальные по рамеру заголовочные файлы (помещая каждое описание структуры в свой отдельный файл и т.д.), у вас легко может получиться нразбериха из сотен файлов.