Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






  • Сервис онлайн-записи на собственном Telegram-боте
    Тот, кто работает в сфере услуг, знает — без ведения записи клиентов никуда. Мало того, что нужно видеть свое расписание, но и напоминать клиентам о визитах тоже. Нашли самый бюджетный и оптимальный вариант: сервис VisitTime.
    Для новых пользователей первый месяц бесплатно.
    Чат-бот для мастеров и специалистов, который упрощает ведение записей:
    Сам записывает клиентов и напоминает им о визите;
    Персонализирует скидки, чаевые, кэшбэк и предоплаты;
    Увеличивает доходимость и помогает больше зарабатывать;
    Начать пользоваться сервисом
  • Бинарные и типизированные фаилы.Общие черты и отличия.Представления данных в них.






    Текстовые и двоичные файлы(типизированные и бинарные)

    Текстовые файлы, с которыми мы имели дело до сих пор, отличаются двумя свойствами:

    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).






    © 2023 :: MyLektsii.ru :: Мои Лекции
    Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
    Копирование текстов разрешено только с указанием индексируемой ссылки на источник.