Предотвращение случайного использования конструктора с одним аргументом в качестве конструктора преобразования

Модуль 3. (2 пары)

Константные методы, explicit конструктора

  1. Константный метод
    1. Синтаксис объявления
    2. Особенности указателя this в константном методе
    3. Примеры использования
  2. Объявление конструктора с использованием ключевого слова explicit
    1. Примеры ситуации, иллюстрирующие неявное создание объекта
    2. Ключевое слово explicit и его использование

Объявление конструктора с использованием ключевого слова explicit

Урок 7

Константные объекты и константные элемент-функции

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

Некоторые объекты должны быть модифицируемыми, а другие нет. Программист может использовать ключевое слово const, чтобы указать, что 

объект не является модифицируемым и что любая попытка изменения этого 

объекта является ошибкой. Оператор

 

const Time noon( 12, 0, 0 );

 

объявляет константный объект noon класса Time и инициализирует его 12 

часами дня.

 

Объявление объекта с модификатором const способствует реализации принципа минимума привилегий. Попытки изменить объект выявляются во время компиляции, а не приводят к ошибкам времени выполнения. Правильное применение const является критическим для правильного проектирования классов, проектирования программ и написания кода.

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

Компиляторы C++ отвергают любые вызовы элемент-функций для константных объектов, если только сама функция не объявлена константной. Это верно даже для get-функций, которые не модифицируют объект. Кроме того, компилятор не позволяет элемент функциям, объявленным как const, модифицировать объект.

Функция специфицируется в качестве константной как в ее прототипе . путем вставки ключевого слова const после списка параметров и, в случае определения функции, перед левой фигурной скобкой, которой начинается тело функции.

 

Определение как const элемент-функции, которая изменяет элемент данных объекта, приводит к ошибке компиляции.

Определение как const элемент-функции, которая вызывает некон- стантную функцию для того же представителя класса, приводит к ошибке компиляции.

Вызов неконстантной элемент-функции для константного объекта приводит к ошибке компиляции.

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

В элемент-функциии, объявленной как const, указатель this тоже является константным.

Здесь возникает интересная проблема в отношении конструкторов и деструкторов; и те и другие, как правило, модифицируют свои объекты. Конструкторы и деструкторы не могут объявляться как const. Конструктор должен иметь возможность модифицировать объект, чтобы последний был правильно инициализирован. Деструктор должен иметь возможность произвести заключительную приборку перед тем, как система освободит память объекта.

Пример 1 представляет класс Time в котором get-функции и функция printUniversal - константные. В заголовочном файле Time.h строки 19-21 и 24 специфицируют теперь ключевое слово const после списка параметров каждой из функций. Соответствующие определения функций (строки 47, 53, 59 и 65) также специфицируют const после списка параметров каждой из функций.

В ф-ии main создаются два объекта Time — не-константный wakeUp (строка 7) и константный noon (строка 8). Программа пытается активировать для константного объекта noon не-константные элемент-функции setHour (строка 13) и printStandard (строка 20). В каждом случае компилятор генерирует сообщение об ошибке. Программа также демонстрирует три других комбинации вызовов элемент-функций с объектами — не-константной функции для неконстантного объекта (строка 11), константной функции для неконстантного объекта (строка 15) и константной функции для константного объекта (строки 17-18). Сообщения об ошибках, генерируемые для вызовов неконстантных функций с константными объектами, показаны в окне вывода. 

Заметьте, что хотя некоторые современные компиляторы выдают только предупреждения для строк 13 и 20 (позволяя, таким образом, запустить программу), мы считаем эти предупреждения ошибками, поскольку стандарт ANSI/ISO C++ не допускает активацию неконстантной элемент-функции для константного объекта.

Обратите внимание, что хотя конструктор должен быть неконстантной элемент-функцией (файл time.cpp, строки 15-18), он все равно может использоваться для инициализации константного объекта (файл fig10_03.cpp, строка 8). Определение конструктора Time (файл time.cpp, строки 15-18) показывает, что конструктор вызывает неконстантную элемент-функцию setTime (строки 21-26), чтобы выполнить инициализацию объекта Time. Вызов неконстантной функции из конструктора в процессе инициализации константного объекта допускается. «Константность» объекта вступает в силу с момента, когда конструктор завершит его инициализацию, и действует вплоть до вызова деструктора объекта.

Заметьте также, что строка 20 (fig10_03.cpp) вызывает ошибку компиляции, хотя функция printStandard класса Time не модифицирует объект, для которого вызывается. Тот факт, что элемент-функция не модифицирует объект, еще не достаточен для того, чтобы функция была константной, — функция явным образом должна быть объявлена как const.

 

Explicit-конструкторы

Пример 2определяет класс arr, который инкасулирует класс целочисленного динамического массива. Поле count содержит число эл-тов массива, int *array – указатель на динамический массив. В классе есть конструктор, в котором распеделяется память, деструктор в котором она освобождается. Константая ф-ия get_count, возвращающая число эл-тов массива.  Константая ф-ия get_arr, возвращающая по индексу значение эл-та массива и ф-ия set_arr, возвращающая ссылку на указанный эл-та массива и используемая для присваивания значений эл-там массива.

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

Программа использует класс arr из Примере 2,чтобы продемонстрировать ошибочное неявное преобразование.

Строка 63 в main создает объект arr с именем mass и вызывает конструктор с одним аргументом с целым значением 100, специфицирующим число элементов в массиве. Вспомните, что конструктор arr, принимающий целый аргумент, инициализирует все элементы массива нулями. Строка 65 вызывает функцию outputArray (определенную в строках 50-57), принимающую в качестве аргумента константную ссылку на arr. Функция выводит число элементов в массиве и его содержимое. В данном случае размер массива равен 100, поэтому выводится сто нулей.

Строка 66 вызывает функцию outputArray с целым значением аргумента 3. Однако в программе нет функции outputArray, которая принимала бы аргумент типа int. Поэтому компилятор выясняет, нет ли в классе arr конструктора преобразования, который может преобразовать int в arr. Поскольку любой конструктор, принимающий единственный аргумент, рассматривается как конструктор преобразования, компилятор полагает, что конструктор arr, принимающий аргумент типа int, является конструктором преобразования, и использует его для преобразования аргумента 3 во временный объект arr, содержащий три элемента. Затем компилятор передает это временный объект функции outputArray для вывода содержимого массива. Таким образом, хотя мы и не предусмотрели в классе явно функции outputArray, принимающей аргумент типа int, компилятор все же может компилировать строку 66. Вывод показывает содержимое трехэлементного массива с тремя нулями.

 

Предотвращение случайного использования конструктора с одним аргументом в качестве конструктора преобразования

 

В C++ имеется ключевое слово explicit, которое позволяет подавить неявные преобразования посредством конструкторов с одним аргументом, когда такие преобразования не должны допускаться. Пример 3 объявляет в классе arr explicit-конструктор. Единственной модификацией в классе arr является добавление ключевого слова explicit к объявлению конструктора с 

одним аргументом в строке 8. Файл исходного кода с определениями элемент-функций класса Array не требует никаких изменений.

Когда данная программа компилируется, компилятор выдает сообщение об ошибке, указывающее, что целое значение, передаваемое функции outputArray в строке 66, не может быть преобразовано в const arr &.Сообщение компилятора об ошибке показано в окне вывода. Строка 67 показывает, как можно использовать explicit-конструктор для создания временного массива из 3-х элементов и передать его функции outputArray.

Попытка вызвать explicit-конструктор для неявного преобразования является ошибкой компиляции.

Применение ключевого слова explicit к элементам данных или элемент-функциям, не являющимся конструкторами с одним аргументом, является ошибкой компиляции.

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


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

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




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