Конструкторы и деструкторы: заключительные замечания
В общих чертах, мы закончили описание конструкторов и деструкторов - важных элементов любого класса. Хотя в дальнейшем нам придётся ещё несколько раз обратиться к этому вопросу, главное уже позади.
И всё же следует сделать несколько замечаний.
Конструктор превращает фрагмент памяти в объект. Посредством операции обращения непосредственно "от имени" объекта можно вызвать функции-члены класса.
Мы можем модифицировать известный нам класс комплексных чисел, определив новую функцию-член reVal(), предназначенную для вывода значения действительной части комплексного числа:
class ComplexType { public: ::::: // Пусть это будет встроенная функция. void reVal(){cout real endl;}; ::::: };
И после определения объекта CDw1, мы можем вызывать эту функцию-член класса. В результате выполнения функции будет выведено значение действительной части объекта CDw1. Важно, что объект используется как точка вызова функции: CDw1.PrintVal();
А вот аналогичного выражения, обеспечивающего неявный вызов конструктора из объекта, как известно, не существует.
CDw1.ComplexType(CDw1); // Неудачная попытка неявного вызова конструктора копирования. // НЕ ОБЪЕКТ ДЛЯ КОНСТРУКТОРА, А КОНСТРУКТОР ДЛЯ ОБЪЕКТА!
По аналогии с конструкторами копирования и преобразования в C++ можно использовать функциональную форму операторов определения переменных основных типов. Синтаксис этих операторов напоминает операторы, содержащие выражения, вычисление которых обеспечивает вызов конструкторов копирования и преобразования:
ComplexType CDw1(125); ComplexType CDw2(CDw1); int iVal1(25); // Соответствует int iVal1 = 25; int iVal2(iVal1); // Соответствует int iVal2 = iVal1;
Конечно же, это не имеет никакого отношения к классам. Но вместе с тем, здесь мы можем наблюдать, как меняется грамматика при введении в язык новых типов: корректное выражение для производных типов по возможности ничем не должно отличаться от выражения для основного типа. Синтаксис операторов определение и инициализации объектов производных типов влияет на синтаксис операторов определения основных типов.
Последнее, что нам осталось сделать - это выяснить причины, по которым в C++ так различаются синтаксис объявления, определения и вызова конструкторов и деструкторов и обычных функций-членов класса.
Причина сокрытия кода регламентных работ по созданию объекта в конструкторе очевидна. Конструктор выполняет сложную работу, связанную с распределением глобальной, локальной и, как будет скоро показано, динамической памяти и превращением фрагмента памяти в объект. Это основания языка. Содержание этого процесса просто обязано быть скрытым от пользователя (программиста) подобно тому, как скрыт программный код, который реализует, например, операцию индексации, сравнения, сдвига, вызов функций и прочие языковые конструкции.
Также скрыты от нас и особенности реализации деструкторов. Не существует даже средства стандартной эффективной проверки результата выполнения деструктора: в некоторых реализациях можно обратиться к функциям-членам объекта даже после уничтожения этого объекта деструктором.
Отсутствие спецификации возвращаемого значения и запрещение операции взятия адреса для конструктора и деструктора также имеют свои объективные причины.
Если бы в их объявлениях присутствовала спецификация возвращаемого значения (неважно, какого) и было бы разрешено применение операции взятия адреса, то можно было бы в программе определять указатели на конструкторы и деструкторы как на обычные функции.
Как известно, указатель на функцию характеризуется типом возвращаемого значения и списком параметров функции. Очевидно, что имя функции в этом случае не играет никакой роли. Но как раз имя конструктора и деструктора и позволяет транслятору различать функции, конструкторы и деструкторы. При использовании указателей для вызова функций, деструкторов и конструкторов транслятор в ряде случаев просто не сможет определить, что, собственно, хочет сделать программист в данном контексте: вызвать функцию или определить новый объект.
Дополнительные ограничения при объявлении и использовании конструкторов полностью устраняют недоразумения, которые могут возникнуть при вызове функций и конструкторов.