ТОП 10:

Указатели на компоненты класса



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

Для объявления указателя на нестатическую функцию используется специальная синтаксическая конструкция, состоящая из спецификатора объявления и заключённого в скобки квалифицированного имени указателя, состоящего из имени класса, операции доступа к члену класса ::, разделителя * , собственно имени указателя, закрывающей скобки и списка параметров: int (XXX::*fp_3) (int);

Подобный указатель может быть проинициализирован инициализатором, состоящим из операции присвоения, операции взятия адреса и квалифицированного имени соответствующей функции-члена:

int (XXX::*fp_3) (int) = &XXX::getVal1;

Вот и нашлась достойная область применения квалифицированным именам.

Как известно, значение унарного выражения, состоящего из операции взятия и операнда, который является именем функции и первичного выражения, состоящего из имени функции эквивалентны. Это адрес данной функции. Поэтому поэтому в качестве инициализирующего выражения для указателя на функцию-член класса также может быть использовано первичное выражение, представляющее собой квалифицированное имя функции-члена: Fp_3 = XXX::getVal2

Класс - это не объект! И не совсем понятно, какое значение имеет адрес нестатичесного члена класса. Значение проинициализированного указателя на нестатическую компоненту остаётся неопределённым.

Оно определяется лишь в результате выполнения операций обращения к членам класса .* и ->* .

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

int val = (q.*fp)(6);

char val = (pq->*fp4)("new string");

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

 

Наследование. Базовый и производный классы.

Наследование является одним из ключевых понятий ООП и подразумевает под собой использование данных и методов одного класса другим классом. При разговоре о наследовании всегда выделяют класс, от которого наследуются свойства - базовый класс, и класс, который наследует - производный класс. Начнем рассмотрение наследования на простых примерах, касающихся одиночного наследования, когда имеется только один базовый класс и произвольное количество производных классов.

Наследование подразумевает использование принципа иерархии и абстрагирования объектов.

 

Инициализация объектов при наследовании

Все действия по инициализации выполняются этап за этапом в порядке наследования классов, т.е. сначала п.1 для класса Object , потом п.1 для следующего класса и т.д., потом п.2 в том же порядке и т.д.

При первом обращении к классу выделяется память под статические поля класса и выполняется их инициализация.Выполняется распределение памяти под создаваемый объект.Выполняются все инициализаторы нестатических полей класса.Выполняется вызов конструктора класса. При создании экземпляра производного класса сначала выполняется конструктор базового, а затем конструктор производного класса. Деструкторы выполняются в обратном порядке. При создании объектов базового класса выполняются конструктор и деструктор только данного базового класса.

 

22. Указатели на производный и базовый классы. Формат явного преобразования указателей на базовый класс.

Для настройки указателя на объект производного класса нам не потребовалось никаких дополнительных преобразований. Здесь важно только одно обстоятельство. Между классами должно существовать отношение наследования. Таким образом, проявляется важное свойство объектно-ориентированного программирования: УКАЗАТЕЛЬ НА БАЗОВЫЙ КЛАСС МОЖЕТ ССЫЛАТЬСЯ НА ОБЪЕКТЫ - ПРОИЗВОДНЫХ КЛАССОВ. Подобное, на первый взгляд, странное обстоятельство имеет своё объяснение. Основной итог этого раздела заключается в следующем: указатель на объект базового класса можно настроить на объект производного типа. Через этот указатель можно "увидеть" лишь фрагмент объекта производного класса - его "базовую" часть - то, что объект получает в наследство от своих предков. Решение о том, какая функция должна быть вызвана, принимается транслятором. В момент выполнения программы всё уже давно решено. Какая функция будет вызвана из объекта производного типа - зависит от типа указателя, настроенного на данный объект. В этом случае мы наблюдаем классический пример статического связывания. Операция :: оставляет в "мёртвой зоне" целые фрагменты объектов. Однако возможность доступа к членам класса, которые оказались вне пределов досягаемости операции доступа всё же существует. Она обеспечивается указателями и операциями явного преобразования типа.

Идея состоит в том, чтобы, объявив указатель на объект-представитель базового класса, попытаться его настроить с помощью операций явного преобразования типа на соответствующий фрагмент объекта производного класса. В результате недосягаемые с помощью операции доступа фрагменты объекта превращаются в безымянные объекты простой конфигурации. Доступ к их членам в этом случае обеспечивается обычными операциями косвенного обращения. Рассмотрим несколько строк, которые демонстрируют такую технику работы с недосягаемыми фрагментами.

Очевидно, что можно обойтись без поэтапных преобразований и воспользоваться свойством коммутативности операции явного преобразования типа:

((A*)(C*)pObjD)->x0 = 5;

((A*)(B*)pObjD)->x0 = 55;

// Разным фрагментам - разные значения.

 

Виртуальный базовый класс

В языке С++ запрещено непосредственно передавать базовый класс в производный более одного раза, т.е. имя класса в списке базовых классов не может повторяться. Однако могут возникать ситуации, когда один производный класс косвенно может наследовать один и тот же базовый класс через другие базовые классы.Такое наследование вполне возможно и компилятор не выдаст сообщение об ошибке, а только предупредит, что наследуемый класс A также находится и в наследуемом классе B. Если один базовый класс косвенно наследуется в одном производном классе и наследуется с атрибутом virtual, то в экземпляр производного класса будет помещена только одна копия базового класса. Базовый класс объявляется как виртуальный только при наследовании - в списке базовых классов с указанием спецификатора virtual:

class A {...};

class B:virtual public A{...};

class C:virtual public A, public B {...};

Следует отметить, что если базовый класс наследуется производным как виртуальный, то его компоненты все равно доступны в производном классе. Отличие между обычным и виртуальным наследованием заключается в том, что когда класс наследует базовый класс более одного раза, то он будет содержать только одно вхождение базового класса.При виртуальном наследовании базовых классов в первую очередь вызываются конструкторы виртуальных базовых классов в порядке наследования. Так, в нашем примере первым будет вызван конструктор виртуального класса А, затем конструктор виртуального класса С, затем конструктор базового класса B, и последним будет вызван конструктор производного класса. Деструкторы вызываются в обратном порядке. В случае, если базовый класс C наследовался бы как не виртуальный, то порядок вызова конструкторов был бы следующий: конструктор класса А, конструктор класса В, конструктор класса С, конструктор класса D. И последнее, если базовый класс А наследовался бы как не виртуальный, то это привело бы к ошибке на стадии компиляции.

 







Последнее изменение этой страницы: 2016-08-26; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.204.227.117 (0.007 с.)