Урок 61. Структуры

   ⁄ 

 Обновлено 31 Авг 2017

  ⁄   

⁄  3

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

Но теперь у вас 6 отдельных независимых переменных. Если вы захотите передать информацию о себе в функцию, вам придется передавать каждую переменную по отдельности. Кроме того, если вы захотите хранить информацию о ком-то еще, вам придется объявить еще 6 переменных для каждого человека дополнительно! Невооруженным глазом видно, что такая реализация не очень эффективна.

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

Объявление и определение структур

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

Мы определили структуру с именем Employee. Она содержит 3 переменные: id типа short, age типа int и salary типа double. Эти переменные, которые являются частью структуры, называются членами (или полями). Employee — это просто объявленная структура, хоть мы и сообщаем компилятору, что она имеет переменные-члены, память под неё сейчас не выделяется. Имена структур принято писать с большой буквы, чтобы отличать их от имен переменных.

Предупреждение. Одна из самых простых ошибок в C++ — забыть точку с запятой в конце объявления структуры. Это приведет к ошибке компилятора в следующей строке кода. Современные компиляторы, такие как Visual Studio 2010 и выше, укажут вам, что вы забыли точку с запятой в конце, но более старые компиляторы могут этого и не сделать, из-за чего такую ошибку будет трудно найти. О том, как установить Visual Studio или какую выбрать IDE мы уже говорили в четвертом уроке.

Чтобы использовать структуру Employee, мы просто объявим переменную типа Employee:

Здесь мы определили переменную типа Employee с именем john. Как и в случае с обычными переменными, определение переменной структуры приведет к выделению памяти под неё.

Объявить можно и несколько переменных одной структуры:

Доступ к членам структур

Когда мы определяем переменную, такую как Employee john, то john ссылается на всю структуру. Для того, чтобы получить доступ к отдельным элементам-полям, используется оператор выбора члена (точка). Ниже приведен пример использования оператора выбора членов для инициализации каждого поля структуры:

Как и в случае с обычными переменными, переменные-поля структуры не инициализируются автоматически и обычно содержат мусор. Инициализировать их нужно вручную.

В примере выше легко определить, какая переменная относится к John, а какая к James. Это обеспечивает гораздо более высокий уровень организации, чем в случае с обычными отдельными переменными.

Переменные-члены структуры работают так же, как и простые переменные, поэтому с ними можно и выполнять обычные операции:

Инициализация структур

Инициализация структур путем присваивания значений каждому члену по порядку – занятие довольно громоздкое (особенно, если этих членов много), поэтому в C++ есть более быстрый способ инициализации структур — список инициализаторов. Он позволяет инициализировать некоторые или все члены структуры во время объявления.

В C++ 11 можно также использовать инициализацию uniform:

Если в списке инициализаторов не будет одного или нескольких элементов, то им присвоятся значения по умолчанию (обычно 0). В примере выше переменной james.salary присвоится значение по умолчанию — 0.0, так как мы сами не указали для нее никакого значения во время инициализации.

C++ 11/14: Не статическая инициализация членов

Начиная с C++ 11, появляется возможность задавать не статистическим (обычным) членам структуры значения по умолчанию:

К сожалению, в C++ 11 синтаксис инициализации не статических полей структуры несовместим с синтаксисом списка инициализаторов или инициализации uniform. В C++ 11 следующая программа не скомпилируется:

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

Однако в C++ 14 это ограничение было отменено, и оба варианта можно использовать. Мы поговорим о статических членах (полях) более подробно в главе 8.

Присваивание значений в структурах

До C++ 11, если бы мы хотели присвоить значения полям структуры, нам пришлось бы это делать вручную индивидуально каждому полю:

Это боль, особенно когда полей в структуре много. В C++ 11 вы можете присваивать значения членам структур, используя список инициализаторов:

Структуры и функции

Большим преимуществом использования структур, нежели отдельных переменных, является возможность передавать всю структуру функции, которая должна работать с её полями:

В примере выше мы передали структуру Employee функции printInformation(). Это позволило нам не передавать каждую переменную по отдельности. Более того, если мы когда-либо захотим добавить новых членов в структуру Employee, нам не придется изменять объявление или вызов функции!

Результат программы выше:

ID:   21
Age:  27
Salary: 28.45

ID:   22
Age:  29
Salary: 19.29

Функция также может возвращать структуру. Это один из тех немногих случаев, когда функция может возвращать несколько переменных.

Результат:

The point is zero

Вложенные структуры

Одни структуры могут содержать другие структуры. Например:

В этом случае, если бы мы хотели узнать, какова зарплата в CEO (исполнительного директора), нам пришлось бы использовать оператор выбора членов дважды:

myCompany.CEO.salary;

Сначала мы выбираем поле CEO из структуры myCompany, а затем поле salary из структуры Employee.

Вы можете использовать вложенные списки инициализаторов для вложенных структур:

Размер структур и выравнивание данных

Как правило, размер структуры — это сумма размеров всех её полей, но не всегда!

Рассмотрим структуру Employee. На большинстве платформ short занимает 2 байта, int — 4, а double — 8, следовательно, ожидается, что Employee будет занимать 2 + 4 + 8 = 14 байтов. Чтобы узнать точный размер Employee, мы можем воспользоваться оператором sizeof:

Результат (на компьютере автора):

The size of Employee is 16

Оказывается, мы можем сказать только, что размер структуры будет, по крайней мере, не меньше суммы размеров всех её переменных. Но он может быть больше! По соображениям производительности компилятор иногда может добавлять пробелы/промежутки в структуры (это называется набивка или заполнение).

В структуре Employee компилятор невидимо добавил 2 байта набивки после элемента id, увеличивая размер структуры к 16 байтам вместо 14. Причина, по которой это происходит, выходит за рамки этого туториала, но если вы хотите знать больше – можете прочитать о выравнивании данных в Википедии. Это необязательно знать и не требуется для прогресса в наших уроках C++.

Доступ к структурам из нескольких файлов

Поскольку объявления структур не занимают память, использовать предварительные объявления для них вы не сможете, но выход есть: если вы хотите использовать объявление структуры в нескольких файлах (чтобы иметь возможность создавать переменные этой структуры в нескольких файлах), поместите объявление структуры в заголовочный файл и #include этот заголовок везде, где необходимо использовать структуру.

Переменные struct подчиняются тем же правилам, что и обычные переменные. Следовательно, если вы хотите сделать переменную структуры доступной в нескольких файлах, вы можете использовать ключевое слово extern.

Итого

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

Тест

1. У вас есть веб-сайт и вы хотите отслеживать, сколько денег вы зарабатываете в день от размещенной рекламы. Объявите структуру Advertising, которая будет отслеживать, сколько объявлений вы показали посетителям (1), сколько процентов посетителей нажали на объявления (2) и сколько вы заработали в среднем за каждое нажатие на объявления (3). Значения этих трех полей должен вводить пользователь. Передайте структуру Advertising функции, которая выведет каждое из значений, а затем подсчитает, сколько всего вы заработали денег за день (умножьте все 3 поля вместе).

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

Ответы

Ответ 1

Ответ 2

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (10 оценок, среднее: 5,00 из 5)
Загрузка...
Поделиться в:
Подписаться на обновления:

Комментариев: 3

  1. Санжар:

    /*Спасибо за Вашу работу!
    Подскажите пожалуйста, а такой вариант приемлем в первом
    задании?*/

    #include

    struct Advertising
    {
    int quantityShowed;
    double percentClick;
    double moneyPerClick;
    };

    int main()
    {
    int x;
    double y,z;
    Advertising aclual{x,y,z};
    std::cin >> x;
    std::cin >> y;
    std::cin >> z;
    std::cout << "You gained " << x*y*z << " dollars.";
    return 0;
    }

    1. Li4ik:

      Пожалуйста 🙂
      Если судить только по результату, то ваш вариант подходит также. Но если с пользовательской точки зрения, то не очень: желательнее всё разбить на функции и описывать действия в программе которые выполняются (например, что значат переменные x, y, z — какие значения пользователю следует вводить).

      1. Санжар:

        Спасибо за ответ, понял.
        Успехов Вам!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *