Как найти адрес перегруженной функции



 

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

В следующем примере программы содержится две версии функции space() . Первая версия выводит на экран count пробелов, а вторая– count символов, переданных в качестве аргумента ch . В функции main() объявляются два указателя на функции. Первый задан как указатель на функцию с одним целочисленным параметром, а второй – как указатель на функцию с двумя параметрами.

 

 

Вот как выглядят результаты выполнения этой программы.

 

 

Как отмечено в комментариях, компилятор способен определить, адрес какой перегруженной функции он получает, на основе того, как объявлены указатели fp1 и fp2 .

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

 

Статические члены класса

 

Один статический член класса разделяется всеми объектами класса.

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

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

Рассмотрим пример использования static ‑члена класса. Изучите код этой программы и постарайтесь понять, как она работает.

 

 

Обратите внимание на то, что статический целочисленный член num объявлен и в классе ShareVar , и определен в качестве глобальной переменной. Как было заявлено выше, необходимость такого двойного объявления вызвана тем, что при объявлении члена num в классе ShareVar память для него не выделяется. C++ инициализирует переменную num значением 0 , поскольку никакой другой инициализации в программе нет. Поэтому в результате двух первых вызовов функции shownum() для объектов а и b отображается значение 0 . Затем объект а устанавливает член num равным 10 , после чего объекты а и b снова выводят на экран его значение с помощью функции shownum() . Но так как существует только одна копия переменной num , разделяемая объектами а и b , значение 10 будет выведено при вызове функции shownum() для обоих объектов.

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

Если static ‑переменная является открытой (т.е. public‑переменной), к ней можно обращаться напрямую через имя ее класса, без ссылки на какой‑либо конкретный объект. (Безусловно, обращаться можно также и через имя объекта.) Рассмотрим, например, эту версию класса ShareVar .

 

 

В данной версии переменная num является public‑членом данных. Это позволяет нам обращаться к ней напрямую, как показано в следующий инструкции.

 

 

Здесь значение переменной num устанавливается независимо от объекта, а для обращения к ней достаточно использовать имя класса и оператор разрешения области видимости. Более того, эта инструкция законна даже до создания каких‑либо объектов типа ShareVar ! Таким образом, получить или установить значение static ‑члена класса можно до того, как будут созданы какие‑либо объекты.

И хотя вы, возможно, пока не почувствовали необходимости в static ‑членах класса, по мере программирования на C++ вам придется столкнуться с ситуациями, когда они окажутся весьма полезными, позволив избежать применения глобальных переменных.

Можно также объявить статической и функцию‑член, но это – нераспространенная практика. К статической функции‑члену могут получить доступ только другие static ‑члены этого класса. (Конечно же, статическая функция‑член может получать доступ к нестатическим глобальным данным и функциям.) Статическая функция‑член не имеет указателя this . Создание виртуальных статических функций‑членов не разрешено. Кроме того, их нельзя объявлять с модификаторами const или volatile . Статическую функцию‑член можно вызвать для объекта ее класса или независимо от какого бы то ни было объекта, а для обращения к ней достаточно использовать имя класса и оператор разрешения области видимости.

 

Применение к функциям‑членам модификаторов const и mutable

 

Константная (const‑) функция‑член не может модифицировать объект, который ее вызвал.

Функции‑члены класса могут быть объявлены с использованием модификатора const . Это означает, что с указателем this в этом случае необходимо обращаться как с const ‑указателем. Другими словами, const ‑функция не может модифицировать объект, для которого она вызвана. Кроме того, const ‑объект не может вызвать не const ‑функцию‑член. Но const ‑функцию‑член могут вызывать как const ‑, так и не const ‑объекты.

Чтобы определить функцию как const ‑член класса, используйте формат, представленный в следующем примере.

 

 

Как видите, модификатор const располагается после объявления списка параметров функции.

Цель объявления функции как const ‑члена – не допустить модификацию объекта, который ее вызывает. Например, рассмотрим следующую программу.

 

 

Эта программа не скомпилируется, поскольку функция seti() объявлена как const ‑член. Это означает, что ей не разрешено модифицировать вызывающий объект. Ее попытка изменить содержимое переменной i приводит к возникновению ошибки. В отличие от функции seti() , функция geti() не пытается модифицировать переменную i , и потому она совершенно приемлема.

Возможны ситуации, когда нужно, чтобы const ‑функция могла изменить один или несколько членов класса, но никак не могла повлиять на остальные. Это можно реализовать с помощью модификатора mutable , который переопределяет атрибут функции const . Другими словами, mutable ‑член может быть модифицирован const ‑функцией‑членом. Рассмотрим пример.

 

 

Здесь член i определен с использованием модификатора mutable , поэтому его можно изменить с помощью функции seti() . Однако переменная j не является mutable ‑членом, поэтому функции setj() не разрешено модифицировать его значение.

 

Использование explicit‑конструкторов

 

Для создания "неконвертирующего" конструктора используйте спецификатор explicit.

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

 

 

Здесь конструктор класса myclass принимает один параметр. Обратите внимание на то, как объявлен объект ob в функции main(). Значение 4, заданное в круглых скобках после имени ob , представляет собой аргумент, который передается параметру x конструктора myclass() , а параметр x в свою очередь используется для инициализации члена a . Именно таким способом мы инициализируем члены класса с начала этой книги. Однако существует и альтернативный вариант инициализации. Например, при выполнении следующей инструкции член a также получит значение 4 .

 

 

Как отмечено в комментарии, этот формат инициализации автоматически преобразуется в вызов конструктора класса myclass , а число 4 используется в качестве аргумента. Другими словами, предыдущая инструкция обрабатывается компилятором так, как если бы она была записана:

 

 

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

Если вам не нужно, чтобы такое неявное преобразование имело место, можно предотвратить его с помощью спецификатора explicit . Ключевое слово explicit применяется только к конструкторам. Конструктор, определенный с помощью спецификатора explicit , будет задействован только в том случае, если для инициализации членов класса используется обычный синтаксис конструктора. Никаких автоматических преобразований выполнено не будет. Например, объявляя конструктор класса myclass с использованием спецификатора explicit , мы тем самым отменяем поддержку автоматического преобразования типов. В этом варианте определения класса функция myclass() объявляется как explicit ‑конструктор.

 

 

Теперь будут разрешены к применению только конструкторы, заданные в таком формате.

 


Дата добавления: 2018-09-22; просмотров: 329; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!