L9 Конструкторы Видимость Пространства имен.ppt
- Количество слайдов: 10
Конструкторы • • • Конструктор - специальная собственная функция, задающая начальное состояние объекта при его создании. В примере показан класс myclock с конструктором, позволяющим установить время при объявлении объекта: myclock a(11, 0, 0); • В отличие от других собственных функций класса, конструктор нельзя вызвать явно, присоединив точкой его имя к имени объекта: a. myclock(11, 59, 30); Неверно!!! • • • Конструктор вызывается только при создании объекта myclock a( 0, 59, 30); //вызывается конструктор и не имеет возвращаемого значения, потому что ему просто некуда его вернуть. • • Чтобы подчеркнуть непохожесть конструктора на другие собственные функции, перед его именем не ставится даже void. • • Конструктор - действительно функция, имеющая параметры, принимающая аргументы и ничего не возвращающая.
• • Как и всякая функция, конструктор в C++ может иметь несколько разновидностей, отличающихся количеством и типом параметров. Если, скажем, объявить объект а типа myclock как myclock a (0, 59); то компилятор станет искать определение конструктора, принимающего два целочисленных аргумента. Если такого конструктора нет, компилятор выдаст сообщение об ошибке и откажется работать дальше. Любопытно, что с новым определением класса уже не проходит объявление объекта myclock «по старинке» : myclock а; Компилятор сообщит о том, что не может вызвать конструктор, то есть функцию myclock(), описанную в классе myclock: no matching function for call to 'myclock: : myclock ()' • Причина сообщения понятна: у нас есть конструктор, принимающий три аргумента, а при объявлении (myclock а; ) должен вызываться конструктор, вообще не имеющий аргументов, а мы такой еще не создали. • (Напомним, что C++ разрешает использовать несколько функций с одинаковыми именами — в том случае, когда список параметров позволяет отличить одну функцию от другой (см. раздел «Функциитезки» главы 4). ) • Но почему же тогда работала программа My. Clock v 1, ведь там тоже использовалось объявление myclock а, а в описании класса myclock вообще не было конструкторов?
• Все дело в том, что классы без конструктора компилятор C++ считает «бедными» и снабжает их «социальным» конструктором, который, правда, ничего не делает, но исправно вызывается при каждом объявлении объекта. • Если же компилятор видит хотя бы один конструктор, то считает, что класс «вырвался из нищеты» и должен заботиться о себе сам. Теперь конструктор, вызываемый по умолчанию при объявлении объекта, нужно создавать самому. • • • Простейший конструктор для наших часов может выглядеть так: class myclock { public: myclock(){min_=hour_=sec_=0: } } Он, как видим, не имеет параметров и поэтому будет вызываться при объявлении объекта (myclock а; ). • В отличие от конструктора, предоставляемого «бедным» классам, этот действует: он устанавливает нулевое время. • Если же переменная объявляется как myclock а(11, 0, 0), вызывается другой конструктор и отсчет времени начинается с одиннадцати часов. • Завершим этот раздел маленьким открытием: оказывается, конструкторы есть абсолютно у всех объектов C++, даже таких примитивных, как целочисленные переменные (типа int). Начальное значение переменной можно указать в скобках, потому что у нее, как и у всякого объекта, есть конструктор: int i(5); •
Видимость объектов • Файл myclock 2. hpp, содержит определение класса myclock с двумя конструкторами — один вызывается по умолчанию и устанавливает нулевое время, второй имеет три параметра и способен задать любые час, минуту и секунду. • • • #include <iostream> #include "myclock 2. hpp" myclock a; • /* Объект а типа myclock объявляется вне функции main() и вне функции nonsense(). Начальное время при этом не указывается, значит, при создании объекта будет вызван конструктор по умолчанию и время станет нулевым 0 часов, 0 минут, 0 секунд). • Объект а типа myclock, о котором мы сейчас говорим, называется глобальным, потому что он виден из любого места программы. При создании глобальных объектов конструктор вызывается только один раз — еще до вызова функции main(). • Глобальный объект а типа myclock видят и функция nonsense(), и функция main ()поэтому на экран дважды будет выведено одно и то же время: 00: 00 • •
• • void nonsense() { • • int main() { • • myclock a(10, 59); cout << "После добавления еще одного объекта myclock a(10, 59); " << endl; • • • nonsense(); // 00: 00 a. disp(); cout << endl; // 10: 59 : : a. disp(); cout << endl; // 00: 00 • Раз в программе объявлены два объекта с одним именем, то непонятно, какое время будет показано после вызова функции nonsense(), а какое — после выполнения следующей инструкции — вызова функции a. disp()? a. disp(); cout << endl; } nonsense(); a. disp(); cout << endl; В этом случае объявляются еще один объект myclock — внутри функции main(). Имена обоих объектов одинаковы, но для инициализации первого используется конструктор по умолчанию, время вторых часов (без секунды одиннадцать) устанавливается другим конструктором.
• Что касается нашей функции nonsense(), то ей виден только глобальный объект myclock, значит, инструкция a. disp() внутри функции nonsense() выведет на экран время 00: 00, задаваемое конструктором по умолчанию. • А вот внутри функции main() объявлен локальный объект myclock а(10, 59), и получается, что функции main() доступны два одноименных объекта, причем один показывает 00: 00, а второй — 10: 59. Какое же время появится на экране? • Запуск программы показал, что это время — без секунды одиннадцать. То есть из двух одноименных объектов функция предпочитает локальный. Но это не значит, что глобальный объект перестал для нее существовать. Чтобы получить доступ к нему, достаточно поставить перед его именем пару двоеточий, называемую оператором расширения области видимости. : : a. disp(); • • • Мы уже встречались с ним при определении собственных функций вне класса. Чтобы показать, что функция set() принадлежит классу myclock, нужно поставить перед именем функции префикс myclock: : set(); • Когда же объект глобален, перед двоеточием ставить нечего, поэтому пишут просто : : a. set(), и это значит, что имеется в виду объект, определенный вне функции main() и вообще вне любой функции. • В отличие от глобальных локальные объекты, определенные внутри функции, создаются при каждом ее вызове. Но когда происходит передача управления из функции, такой объект перестает существовать, а при следующем вызове создается вновь. Естественно, конструктор такого объекта вызывается при каждом его создании, то есть при каждом вызове функции.
Пространства имен • Объявляя объекты, нужно следить за тем, чтобы у них были разные имена. И это становится трудно сделать, когда объектов много или когда ими занимаются разные люди. Кроме того, есть объекты, имена которых менять не хочется, например функция, вычисляющая косинус, обычно называется cos(), а функцию, выводящую что-то на экран, разумно назвать print(). • Для всех этих случаев в C++ существуют пространства имен, задаваемые ключевым словом namespace. • • • #include <iostream> namespace xxx { void show() { std: : cout << "namespace xxx" << std: : endl; } } namespace zzz { void show() { std: : cout << "namespace zzz" << std: : endl; } }
• В программе создаются две функции show(): одна в пространстве имен ххх, другая — в пространстве zzz. Теперь можно вызвать нужную функцию, указав оператором : : подходящее пространство имен. Инструкция xxx: : show(): вызовет функцию show. O из пространства имен ххх, которая выведет на экран слова «namespace xxx» . • Обратите внимание: перед именами cout и endl стоит признак стандартного пространства имен std: : . Раньше мы предпочитали использовать директиву using namespace std; говорящую компилятору, что все имена без явного указания пространства относятся к std. • • • int main() { xxx: : show(); //namespace xxx zzz: : show(); //namespace zzz • Естественно, директиву using namespace. . . можно применить к любому пространству имен. Используя по умолчанию пространство имен ххх, функция show. O, записанная без «опознавательных знаков» , выведет на экран «namespace xxx» , потому что компилятор сообразит, что она относится к пространству имен ххх. • • • using namespace xxx; show(); //namespace xxx system("PAUSE"); return EXIT_SUCCESS; }
Статические объекты • Кроме локальных и глобальных переменных существуют еще статические объекты, которые создаются один раз (соответственно, и конструктор таких объектов вызывается один раз) и помнят свое прежнее состояние. • • • #include <iostream> • • • int main(){ nonsense(); // i=? j=0 nonsense(); // i=? j=3 • • • system("PAUSE"); return 0; } using namespace std; void nonsense() { int i; static int j; cout << "i=" << i << endl; cout << "j=" << j << endl; i = 1; j = 3; }
• При первом вызове функции nonsense() обычная переменная i содержит «мусор» , то есть то, что было в памяти компьютера до того, как ее заняла переменная. В отличие от i статическая переменная j содержит ноль, то есть все ее биты равны нулю. • После выполнения инструкций i = 1; и j = 3; обе переменные получают определенные значения, но после выхода из функции значение автоматической переменной i забывается, а значение статической j — нет. • • И при следующем вызове функции nonsense() значение i может быть каким угодно, в том числе 1, а значение j гарантированно равно 3. • Происходит так потому, что статическая переменная статична — она создается один раз и остается в одном месте компьютерной памяти до полного завершения программы. • Обычные же или автоматические переменные живут только во время работы функции. Они создаются при каждом ее вызове и уничтожаются при каждом возврате из функции. • Поскольку статическая переменная всегда занимает одно и то же место в памяти, она не может иметь разные значения в разных объектах одного класса. Для всех таких объектов она общая. Значит, создать ее и задать начальные значения (если это действительно переменная) можно только вне класса!
L9 Конструкторы Видимость Пространства имен.ppt