Основы программирования на С++ для начинающих

Указатели на объекты

При первом знакомстве с указателями в C++ (см. Указатели в C++. Часть 1 ) может сложиться упрощённое представление, что указатели могут указывать только на отдельные переменные встроенных (скалярных) типов C++, и что это просто ещё одна, альтернативная форма доступа к таким переменным. В таком применении указатели были бы приятным дополнением языка, но с весьма ограниченными возможностями.

При более внимательном изучении указателей C++ мы обнаруживаем, что указатель может быть адресом размещения (указывать на) любого допустимого объекта в программе: структуры, объекта класса, массива, функции, или опять же указателя на некоторый объект, или указателя на указатель и так далее… Это делает указатели чуть ли не самым мощным инструментом программиста на C++ … но и самым опасным в смысле возможных скрытых ошибок.

Рассмотрим такие варианты подробнее. Самым простым вариантом будет использование указателей на составные объекты (объекты классов и структуры). Но уже такое использование указателей открывает широкие перспективы в C++, как мы сейчас увидим.

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

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

Здесь мы видим относительно новую в наших темах конструкцию:

Это конструктор нового класса my, но с параметром создания. При вызове он вызывает конструктор родительского класса (number(numb)), передавая ему это же значение параметра. Следующие далее скобки {} обрамляют пустой блок кода, который означает, что ничего более сверх вызова родительского конструктора делать не нужно.

Вспоминаем, что вся последовательность конструкторов всех родительских классов – вызывается (в порядке обратном наследованию) при вызове конструктора порождённого класса, но это только для конструкторов без параметров. В случае параметризированных конструкторов родиелей вам прийдётся вызывать явно.

Но мы на этом отвлеклись в сторону от предмета нашего изложения… А теперь самое время компилировать и посмотреть выполнение полученной нами программы:

с++, указатели на объекты, программирование для начинающих, c++

Пока ничего принципиально нового, и всё это сильно похоже на то, как мы работали бы с указателями на переменные типа double, скажем.

Вспомним, в дополнение, что оператор new для динамического создания нового объекта:

а) вызывает менеджер динамического управления памяти и выделяет новый объем под размещение такого объекта;

б) вызывает конструктор соответствующего класса (типа данных) для начальной разметки (инициализации) выделенной памяти. Слегка модифицируем свой пример (файл ex2.cc):

Сборка и выполнение:

с++, указатели на объекты, программирование для начинающих, c++

Пока ещё не произошло никаких радикальных изменений …, но обратим внимание на то, что в программе (а это могла бы быть сколь угодно большая программа) вообще не объявлены и не используются в явном виде объекты — программа оперирует только указателями на объекты.

И, наконец, мы приближаемся к той технике в использовании указателей на объекты, которая делает указатели самым мощным инструментом работы с классами и их объектами в C++. А именно: виртуализация функций-членов класса и полиморфизм.

Понятие полиморфизма (изменчивости формы, многоликости) – одно из основных направлений развития C++ от его предшественника языка C. Оно состоит в том, что прародитель целого семейства наследуемых классов объявляет некоторые свои функции-методы как virtual. А в разных наследуемых классах эти функции-методы переопределяются по-разному.

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

Продемонстрируем эту технику на примере. Создадим (файл ex3.cc) класс, описывающий вообще любые плоские фигуры на плоскости (это могут быть элементы некоторого 2D игрового сценария):

Здесь перед нами образец того, что в C++ называется абстрактный класс: класс, в котором определена хотя бы одна виртуальная функция с определением вида:

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

Определим 3 производных от figure класса: круг, квадрат и равносторонний треугольник:

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

На этом простейшем примере показано то, что в объектной модели языка C++ называется полиморфизм. И это свойство является одним из самых мощных выразительных инструментов языка C++. И реализуется эта техника всегда через указатели на объекты (figure*).

Вот как будет выглядеть компиляция и выполнение нашего примера (ex3.cc) в терминале операционной системы Linux при использовании GCC компилятора с языка C++ (это будет лишний раз подтверждением того, что программирование на языке C++ в меньшей мере зависит от операционной системы):

с++, указатели на объекты, программирование для начинающих, c++

Ещё раз обратимся к коду показанного примера, и лишний раз зафиксируем то чрезвычайно важное обстоятельство, что указатели C++ всегда типизированы: указатель не может быть «указателем на что-то». Язык C++ – это язык со строгой именной типизацией. Типом же указателя является: указатель на тип указываемой ним переменной, например «указатель на double». Указатели на различные типы несовместимы между собой по присвоению и сравнению.

Техника виртуальных функций и полиморфизма являются настолько основными для всей философии C++, что требуют отдельного подробного рассмотрения. Об это поговорим в одном из следующих уроков.

11 thoughts on “Указатели на объекты

  1. По теме – указатель указывает на начало области памяти (хранит адрес). И нечего дальше писать про это, т.к. при использовании указателя ты просто берешь значение по этому адресу и приводишь его к нужному типу (любому).
    Если и раскрывать тему – то надо ИМХО написать про то, какие проблемы решали указатели (не касаясь ООП, т.к. тут эта тема почти не раскрывалась) и написать по мотивам указатели vs ссылки (тема уже раскрыта у Маерса, можно взять там основу).

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

    В конце статьи ты описала полиморфизм – я думаю это стоит выделить в отдельную статью. Но перед этим написать статью про “Наследование классов в C++” (это последняя тема в твоем плане на главной странице)

    1. Вот согласен. В начале про указатели, а потом какогото фига про полиморфизм. Как-то слишком в кучу всё.

  2. Скажите, а кто админ? Лилия или Игорь? Я всегда думал, что от имени админа пишет Игорь.

    1. Объект класса (типа) my – m1.
      Указатель на этот объект – pm1.
      m1.show() – вызов метода (функции) show() класса my для объекта m1.
      pm1->show() – вызов точно того же метода show() класса my через указатель на объект m1.

      1. Вообще-то m1 – это один объект, а pm1 – это указатель на совсем другой объект того же класса (в динамической памяти).

  3. Bы пoбeдили 93 903 Pyблeй >>> https://forms.yandex.ru/cloud/6547f889068ff0ecfdb5873a/?hs=7547a39eb399d3a1f3aca107d969725b& # YU386:

    1p1rlh

  4. Подскажите как устранить эту ошибку?
    Ошибка (активно) E0020 идентификатор “M_PI” не определен Project1

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

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