C++
C++ читать книгу онлайн
С++ – это универсальный язык программирования, задуманный так, чтобы сделать программирование более приятным для серьезного программиста. За исключением второстепенных деталей С++ является надмножеством языка программирования C. Помимо возможностей, которые дает C, С++ предоставляет гибкие и эффективные средства определения новых типов. Используя определения новых типов, точно отвечающих концепциям приложения, программист может разделять разрабатываемую программу на легко поддающиеся контролю части. Такой метод построения программ часто называют абстракцией данных. Информация о типах содержится в некоторых объектах типов, определенных пользователем. Такие объекты просты и надежны в использовании в тех ситуациях, когда их тип нельзя установить на стадии компиляции. Программирование с применением таких объектов часто называют объектно-ориентированным. При правильном использовании этот метод дает более короткие, проще понимаемые и легче контролируемые программы.
Ключевым понятием С++ является класс. Класс – это тип, определяемый пользователем. Классы обеспечивают сокрытие данных, гарантированную инициализацию данных, неявное преобразование типов для типов, определенных пользователем, динамическое задание типа, контролируемое пользователем управление памятью и механизмы перегрузки операций. С++ предоставляет гораздо лучшие, чем в C, средства выражения модульности программы и проверки типов. В языке есть также усовершенствования, не связанные непосредственно с классами, включающие в себя символические константы, inline-подстановку функций, параметры функции по умолчанию, перегруженные имена функций, операции управления свободной памятью и ссылочный тип. В С++ сохранены возможности языка C по работе с основными объектами аппаратного обеспечения (биты, байты, слова, адреса и т.п.). Это позволяет весьма эффективно реализовывать типы, определяемые пользователем.
С++ и его стандартные библиотеки спроектированы так, чтобы обеспечивать переносимость. Имеющаяся на текущий момент реализация языка будет идти в большинстве систем, поддерживающих C. Из С++ программ можно использовать C библиотеки, и с С++ можно использовать большую часть инструментальных средств, поддерживающих программирование на C.
Эта книга предназначена главным образом для того, чтобы помочь серьезным программистам изучить язык и применять его в нетривиальных проектах. В ней дано полное описание С++, много примеров и еще больше фрагментов программ.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Эта таблица отличается от той, которая определена в Глве 3 тем, что это настоящий тип. Можно описать более чем одну
table, можно иметь указатель на table и т.д. Например:
#include «table.h»
table globals; table keywords; table* locals;
main() (* locals = new table; // ... *)
Вот реализация table::look(), которая использует линеный поиск в связанном списке имен name в таблице:
#include «string.h»
name* table::look(char* p, int ins) (* for (name* n = tbl; n; n=n-»next) if (strcmp(p,n-»string) == 0) return n;
if (ins == 0) error(«имя не найдено»);
name* nn = new name; nn-»string = new char[strlen(p)+1]; strcpy(nn-»string,p); nn-»value = 1; nn-»next = tbl; tbl = nn; return nn; *)
Теперь рассмотрим класс table, усовершенствованный таким образом, чтобы использовать хэшированный просмотр, как это делалось в примере с настольным калькулятором. Сделать это труднее из-за того ограничения, что уже написанные программы, в которых использовалась только что определенная версия класа table, должны оставаться верными без изменений:
class table (* name** tbl; int size; public: table(int sz = 15); ~table();
name* look(char*, int = 0); name* insert(char* s) (* return look(s,1); *) *);
В структуру данных и конструктор внесены изменения, оражающие необходимость того, что при использовании хэшировния таблица должна иметь определенный размер. Задание конструктора с параметром по умолчанию обеспечивает, что страя программа, в которой не указывался размер таблицы, остнется правильной. Параметры по умолчанию очень полезны в стуации, когда нужно изменить класс не повлияв на старые программы. Теперь конструктор и деструктор создают и уничтжают хэш-таблицы:
table::table(int sz) (* if (sz « 0) error(„отрицательный размер таблицы“); tbl = new name*[size=sz];
for (int i = 0; i«sz; i++) tbl[i] = 0; *)
table::~table() (* for (int i = 0; i«size; i++) for (name* n = tbl[i]; n; n=n-»next) (* delete n-»string; delete n; *) delete tbl; *)
Описав деструктор для класса name можно получить более простой и ясный вариант table::~table(). Функция просмотра практически идентична той, которая использовалась в примере настольного калькулятора (#3.1.3):
#include «string.h»
name* table::look(char* p, int ins) (* int ii = 0; char* pp = p; while (*pp) ii = ii««1 ^ *pp++; if (ii « 0) ii = -ii; ii %= size;
for (name* n=tbl[ii]; n; n=n-»next) if (strcmp(p,n-»string) == 0) return n;
if (ins == 0) error(«имя не найдено»);
name* nn = new name; nn-»string = new char[strlen(p)+1]; strcpy(nn-»string,p); nn-»value = 1; nn-»next = tbl[ii]; tbl[ii] = nn; return nn;
*)
Очевидно, что функции члены класса должны заново комплироваться всегда, когда вносится какое-либо изменение в опсание класса. В идеале такое изменение никак не должно отржаться на пользователях класса. К сожалению, это не так. Для размещения переменной классового типа компилятор должен знать размер объекта класса. Если размер этих объектов меняется, то файлы, в которых класс используется, нужно компилировать зново. Можно написать такую программу (и она уже написана), которая определяет множество (минимальное) файлов, которое необходимо компилировать заново после изменения описания класса, но пока что широкого распространения она не получила.
Почему, можете вы спросить, С++ разработан так, что поле изменения закрытой части необходима новая компиляция ползователей класса? И действительно, почему вообще закрытая часть должна быть представлена в описании класса? Другими словами, раз пользователям класса не разрешается обращаться к закрытым членам, почему их описания должны приводиться в зголовочных файлах, которые, как предполагается, пользователь читает? Ответ – эффективность. Во многих системах и процесс компиляции, и последовательность операций, реализующих вызов функции, проще, когда размер автоматических объектов (объетов в стеке) известен во время компиляции.
Этой сложности можно избежать, представив каждый объект класса как указатель на «настоящий» объект. Так как все эти указатели будут иметь одинаковый размер, а размещение «настящих» объектов можно определить в файле, где доступна закртая часть, то это может решить проблему. Однако решение поразумевает дополнительные ссылки по памяти при обращении к членам класса, а также, что еще хуже, каждый вызов функции с автоматическим объектом класса включает по меньшей мере один вызов программ выделения и освобождения свободной памяти. Это сделало бы также невозможным реализацию inline-функций члнов, которые обращаются к данным закрытой части. Более того, такое изменение сделает невозможным совместную компоновку C и С++ программ (поскольку C компилятор обрабатывает struct не так, как это будет делать С++ компилятор). Для С++ это было сочтено неприемлемым.
5.3.2 Законченный класс
Программирование без сокрытия данных (с применением структур) требует меньшей продуманности, чем программирование со сокрытием данных (с использованием классов). Структуру можно определить не слишком задумываясь о том, как ее предплагается использовать. А когда определяется класс, все внимние сосредотачивается на обеспечении нового типа полным мнжеством операций; это важное смещение акцента. Время, потраченное на разработку нового типа, обычно многократно окупается при разработке и тестировании программы.
Вот пример законченного типа intset, который реализует понятие «множество целых»:
class intset (* int cursize, maxsize; int *x; public: intset(int m, int n); // самое большее, m int'ов в 1..n ~intset();
int member(int t); // является ли t элементом? void insert(int t); // добавить "t" в множество
void iterate(int amp; i) (* i = 0; *) int ok(int amp; i) (* return i«cursize; *) int next(int amp; i) (* return x[i++]; *) *);
Чтобы протестировать этот класс, можно создать и распчатать множество случайных целых чисел. Такое множество могло бы быть результатом розыгрыша лотереи. Это простое множество можно также использовать для проверки последовательности цлых на повторы. Но для большинства приложений тип множество должен быть немного более проработанным. Как всегда, возможны ошибки:
#include «stream.h»
void error(char* s) (* cerr «„ "set: " «« s «« «n“; exit(1); *)
Класс intset используется в main(), которая предполагает два целых параметра. Первый параметр задает число случайных чисел, которые нужно сгенерировать. Второй параметр указывает диапазон, в котором должны лежать случайные целые:
main(int argc, char* argv[]) (* if (argc != 3) error(«ожидается два параметра»); int count = 0; int m = atoi(argv[1]); // число элементов множества int n = atoi(argv[2]); // в диапазоне 1..n intset s(m,n);
while (count«m) (* int t = randint(n); if (s.member(t)==0) (* s.insert(t); count++; *) *)
print_in_order( amp;s); *)
В программе, для которой требуется два параметра, счечик числа параметров, argc, должен равняться трем, потому что имя программы всегда передается как argv[0]. Функция
extern int atoi(char*);
функция atoi() это стандартная библиотечная функция для преобразования представления целого в виде строки в его внуреннюю (двоичную) форму. Случайные числа генерируются с пмощью стандартной функции rand():
extern int rand(); // Не очень случайные, будьте осторожны
int randint(int u) // в диапазоне 1..u (* int r = rand(); if (r « 0) r = -r; return 1 + r%u ; *)
Подробности реализации класса должны представлять для пользователя весьма незначительный интерес, но здесь в любом случае будут функции члены. Конструктор выделяет целый вектор заданного максимального размера множества, а деструктор освбождает его:
intset::intset(int m, int n)//самое большее,m int'ов в 1..n (* if (m«1 !! n„m) error(«недопустимый размер intset“); cursize = 0; maxsize = m; x = new int[maxsize]; *)
intset::~intset() (* delete x; *)