Главная страница Случайная страница Разделы сайта АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
💸 Как сделать бизнес проще, а карман толще?
Тот, кто работает в сфере услуг, знает — без ведения записи клиентов никуда. Мало того, что нужно видеть свое раписание, но и напоминать клиентам о визитах тоже.
Проблема в том, что средняя цена по рынку за такой сервис — 800 руб/мес или почти 15 000 руб за год. И это минимальный функционал.
Нашли самый бюджетный и оптимальный вариант: сервис VisitTime.⚡️ Для новых пользователей первый месяц бесплатно. А далее 290 руб/мес, это в 3 раза дешевле аналогов. За эту цену доступен весь функционал: напоминание о визитах, чаевые, предоплаты, общение с клиентами, переносы записей и так далее. ✅ Уйма гибких настроек, которые помогут вам зарабатывать больше и забыть про чувство «что-то мне нужно было сделать». Сомневаетесь? нажмите на текст, запустите чат-бота и убедитесь во всем сами! Бинарные и типизированные фаилы.Общие черты и отличия.Представления данных в них.
Текстовые и двоичные файлы(типизированные и бинарные) Текстовые файлы, с которыми мы имели дело до сих пор, отличаются двумя свойствами: 1.файлы делятся на строки, и конец строки отмечается специальным символом «new line» (новая строка), роль которого в системе ввода/вывода С++ играет ‘\n’; 2.при вводе чисел выполняется преобразование из символьного вида во внутренний формат (вспомните функцию atoi — см. листинг 3.12); при выводе чисел — из внутреннего формата в символьный; Двоичные файлы, как вы понимаете, этими свойствами не обладают. Это означает, что двоичные файлы на строки не делятся, и при вводе/выводе никаких преобразований не делается. При операции записи в двоичный файл попадает ровно столько байтов, сколько записываемый объект занимает в памяти. Например, целое число, записанное в двоичный файл, займет на диске sizeof(int) байтов. Это существенно отличается от записи в текстовый файл, где количество записываемых по умолчанию символов зависит от величины числа. Например, число 1 в текстовом файле займет 1 байт, а 55555 — 5 байтов. Примерами двоичных файлов могут служить файлы выполняемых программ (с расширением exe). Чтобы прочувствовать разницу, давайте напишем программу, которая генерируют 1000 целых случайных чисел и выводит их в файлы. Один файл будет текстовым, а другой — двоичным. Тут нас поджидает одна проблема: в классах fstream, ifstream, ofstream отсутствуют методы ввода/вывода целых чисел в файл. Однако нам нет необходимости писать собственные функции, если мы умеем работать с указателями. Первый параметр методов read/write — это char *. Поэтому мы можем непосредственно обращаться к этим методам, используя подходящее преобразование указателей. Посмотрите, как это делается для типа long; from — входной поток, to — выходной поток (листинг 6.15). //Листинг 6.15. Ввод/вывод встроенных типов данных long a; from.read((char *)& a, sizeof(long)); to.write((char *)& a, sizeof(long)); Преобразование типов можно делать и в стиле С++: to.write(reinterpret_cast< char *> (& a), sizeof(long)); from.read(reinterpret_cast< char *> (& a), sizeof(long)); 20.Пользовательские типы данных.Структуры.Массивы структур/записей. К пользовательским типам данных относятся нестандартные данные, о структуре которых система не имеет представления, и операции над которыми стандартом языка C не определены. Структура таких данных становится известной компилятору только по описанию, содержащемуся в тексте исходной программы. К пользовательским данным такого типа относятся массивы (о них речь шла в предыдущем разделе), структуры (в других алгоритмических языках они известны под термином записи), перечисления (в некоторых книгах по языкам C, C++ их относят к целочисленным данным) и объединения. 9.1. Структуры Первоначальным образом для данных типа структур явились строки таблиц, с которыми знаком любой человек. Характерным для таблиц любого содержания является наличие столбцов, в каждом из которых хранятся однотипные данные. Однако в соседних столбцах типы данных могут отличаться. Если специфической особенностью массивов является использование одного и того же типа для всех элементов массива, то строки таблиц можно представлять как последовательность полей данных разного типа. Для каждого поля строки таблицы известно наименование соответствующего столбца таблицы и тип размещаемого в этом поле значения. Например, поле " Фамилия" заполняется текстовой информацией, поле " Год рождения" хранит целочисленные данные, на поле " Пол" достаточно записывать единственный символ 'М' или 'Ж' и т.д. 9.1.1. Объявление и инициализация структур То, что принято называть " шапкой" таблицы в языках программирования носит название шаблона структуры. Например, шаблон структуры, описывающей данные о книге, может быть устроен следующим образом: struct book { char title[40]; //наименование char authors[30]; //авторы char publishing_house[15]; //издательство int year; //год издания int pages; //количество страниц float price; //цена } Для доступа к соответствующим полям структур используются составные имена, образованные из имени структуры и имени поля: strcpy(b1.title, " C++: Экспресс курс"); strcpy(b1.authors, " В.Лаптев"); cout < < b1.title; b1.year=2004; Если мы имеем дело с указателем, который настроен на адрес структуры, то составные имена записываются с использованием двух символов " -> ": struct book *p=& b1; //указатель на структуру strcpy(p-> title, " C++: Экспресс курс" strcpy(p-> authors, " В.Лаптев"); cout < < p-> title; p-> year=2004; Структуры могут объединяться в массивы: struct book c[20]; //массив структур И тогда для доступа к полям соответствующего элемента используются индексированные составные имена: strcpy(с[2].title, " Язык C++ " strcpy(c[2].authors, " В.В.Подбельский"); cout < < c[2].title; c[2].year=1996; struct book *pс=с; strcpy(pс[2]-> title, " C++: Экспресс курс" strcpy(pс[2]-> authors, " В.Лаптев"); cout < < pс[2]-> title; pс[2]-> year=2004; 23.Перегрузка операций для структур: определение правил для операторов согласно разработанным пользовательским типам. этой главе описывается аппарат, предоставляемый в C++ для перегрузки операций. Программист может определять смысл операций при их применении к объектам определенного класса. Кроме арифметических, можно определять еще и логические операции, операции сравнения, вызова () и индексирования [], а также можно переопределять присваивание и инициализацию. Можно определить явное и неявное преобразование между определяемыми пользователем и основными типами. Показано, как определить класс, объект которого не может быть никак иначе скопирован или уничтожен кроме как специальными определенными пользователем функциями. 6.1 Введение Часто программы работают с объектами, которые являются конкретными представлениями абстрактных понятий. Например, тип данных int в C++ вместе с операциями +, -, *, / и т.д. предоставляет реализацию (ограниченную) математического понятия целых чисел. Такие понятия обычно включают в себя множество операций, которые кратко, удобно и привычно представляют основные действия над объектами. К сожалению, язык программирования может непосредственно поддерживать лишь очень малое число таких понятий. Например, такие понятия, как комплексная арифметика, матричная алгебра, логические сигналы и строки не получили прямой поддержки в C++. Классы дают средство спецификации в C++ представления неэлементарных объектов вместе с множеством действий, которые могут над этими объектами выполняться. Иногда определение того, как действуют операции на объекты классов, позволяет программисту обеспечить более общепринятую и удобную запись для манипуляции объектами классов, чем та, которую можно достичь используя лишь основную функциональную запись. Например: class complex { double re, im; public: complex(double r, double i) { re=r; im=i; } friend complex operator+(complex, complex); friend complex operator*(complex, complex); }; определяет простую реализацию понятия комплексного числа, в которой число представляется парой чисел с плавающей точкой двойной точности, работа с которыми осуществляется посредством операций + и * (и только). Программист задает смысл операций + и * с помощью определения функций с именами operator+ и operator*. Если, например, даны b и c типа complex, то b+c означает (по определению) operator+(b, c). Теперь есть возможность приблизить общепринятую интерпретацию комплексных выражений. Например: void f(){ complex a = complex(1, 3.1); complex b = complex(1.2, 2); complex c = b; a = b+c; b = b+c*a; c = a*b+complex(1, 2); } Выполняются обычные правила приоритетов, поэтому второй оператор означает b=b+(c*a), а не b=(b+c)*a. 6.2 Функции Операции Можно описывать функции, определяющие значения следующих операций:
+ - * / % ^ & | ~! = += -= *= /= %= ^= & = |= > > > = = & & || ++ -- [] () new delete Последние четыре - это индексирование (#6.7), вызов функции (#6.8), выделение свободной памяти и освобождение свободной памяти (#3.2.6). Изменить приоритеты перечисленных операций невозможно, как невозможно изменить и синтаксис выражений. Нельзя, например, определить унарную операцию % или бинарную!. Невозможно определить новые лексические символы операций, но в тех случаях, когда множество операций недостаточно, вы можете использовать запись вызова функции. Используйте например, не **, а pow(). Эти ограничения могут показаться драконовскими, но более гибкие правила могут очень легко привести к неоднозначностям. Например, на первый взгляд определение операции **, означающей возведение в степень, может показаться очевидной и простой задачей, но подумайте еще раз. Должна ли ** связываться влево (как в Фортране) или вправо (как в Алголе)? Выражение a**p должно интерпретироваться как a*(*p) или как (a)**(p)? Имя функции операции есть ключевое слово operator (то есть, операция), за которым следует сама операция, например, operator void f(complex a, complex b) { complex c = a + b; // сокращенная запись complex d = operator+(a, b); // явный вызов } 6.2.1 Бинарные и Унарные Операции Бинарная операция может быть определена или как функция член, получающая один параметр, или как функция друг, получающая два параметра. Таким образом, для любой бинарной операции @ aa@bb может интерпретироваться или как aa.operator@(bb), или как operator@(aa, bb). Если определены обе, то aa@bb является ошибкой. Унарная операция, префиксная или постфиксная, может быть определена или как функция член, не получающая параметров, или как функция друг, получающая один параметр. Таким образом, для любой унарной операции @ aa@ или @aa может интерпретироваться или как aa.operator@(), или как operator@(aa). Если определена и то, и другое, то и aa@ и @aa являются ошибками. Рассмотрим следующие примеры: class X { // друзья friend X operator-(X); // унарный минус friend X operator-(X, X); // бинарный минус friend X operator-(); // ошибка: нет операндов friend X operator-(X, X, X); // ошибка: тернарная // члены (с неявным первым параметром: this) X* operator& (); // унарное & (взятие адреса) X operator& (X); // бинарное & (операция И) X operator& (X, X); // ошибка: тернарное }; Когда операции ++ и -- перегружены, префиксное использование и постфиксное различить невозможно. 6.2.3 Операции и Определяемые Пользователем Типы Функция операция должна или быть членом, или получать в качестве параметра по меньшей мере один объект класса (функциям, которые переопределяют операции new и delete, это делать необязательно). Это правило гарантирует, что пользователь не может изменить смысл никакого выражения, не включающего в себя определенного пользователем типа. В частности, невозможно определить функцию, которая действует исключительно на указатели. Функция операция, первым параметром которой предполагается основной тип, не может быть функцией членом. Рассмотрим, например, сложение комплексной переменной aa с целым 2: aa+2, при подходящим образом описанной функции члене, может быть проинтерпретировано как aa.operator+(2), но с 2+aa это не может быть сделано, потому что нет такого класса int, для которого можно было бы определить + так, чтобы это означало 2.operator+(aa). Даже если бы такой тип был, то для того, чтобы обработать и 2+aa и aa+2, понадобилось бы две различных функции члена. Так как компилятор не знает смысла +, определенного пользователем, то не может предполагать, что он коммутативен, и интерпретировать 2+aa как aa+2. С этим примером могут легко справиться функции друзья. Все функции операции по определению перегружены. Функция операция задает новый смысл операции в дополнение к встроенному определению, и может существовать несколько функций операций с одним и тем же именем, если в типах их параметров имеются отличия, различимые для компилятора, чтобы он мог различать их при обращении (см. #4.6.7).
|