Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 59
Текст из файла (страница 59)
p u t b u f ( ) ) ; // вывод символовol.show() ;return 0;4. Статические функции-члены применяются достаточно редко, но для предварительной (до создания реального объекта) инициализации закрытых статических данных-членов они могут оказаться очень удобными. Например, ниже представлена совершенно правильная программа.^include <iostream>using namespace std;class static__func_demo {static int i;public:static void init(int x) { i = x; Jvoid show() { cout « i; }};int static_func_demo::i; // определение переменной iint main()(// инициализация статических данных еще до создания объектаstatic_func_demo::init(100);static_func_demo x;x.show{); // вывод на экран значения 100return 0;Здесь вызов функции init() инициализирует переменную i еще до созданияобъекта типа static func demo.Глава 13.
Пространства имен и другие темы40 JУпражненияения]1. Переделайте пример 3 так, чтобы на экране отображался тот объект, которыйосуществляет вывод символов, и тот объект или те объекты, для которых изза занятости буфера вывод запрещен.2. Одним из интересных применений статических переменных-членов являетсяхранение информации о количестве объектов класса, существующих в каждый конкретный момент времени. Для этого необходимо увеличивать наединицу статическую переменную-член каждый раз, когда вызывается конструктор класса, и уменьшать на единицу, когда вызывается деструктор.
Реализуйте эту схему и продемонстрируйте ее работу.13.4. Постоянные и модифицируемыечлены классаФункции — члены класса могут объявляться постоянными (с идентификатором const). Если функция объявлена постоянной, она не может изменитьвызывающий ее объект. Кроме этого, постоянный объект не может вызватьнепостоянную функцию-член. Тем не менее, постоянная функция-член может вызываться как постоянными, так и непостоянными объектами.Для задания постоянной функции-члена используйте ее форму, представленную в следующем примере:class X {int some_var;public:•int fl() const; // постоянная функция-членОбратите внимание, что ключевое слово const указывают следом за спискомпараметров функции, а не перед именем функции.Возможна ситуация, когда вам понадобится, чтобы функция-член, оставаясьпостоянной, тем не менее была способна изменить один или несколькочленов класса.
Это достигается заданием модифицируемых членов класса(ключевое слово mutable). Модифицируемый член класса можно изменить спомощью постоянной функции-члена.1. Функция-член объявляется постоянной, чтобы предотвратить возможностьизменения вызвавшего ее объекта. Для примера рассмотрим следующуюпрограмму._402СамоучительC++/* Пример объявления постоянных функций-членов. Данная программасодержит ошибку и компилироваться не Судет*/#include <iostream>using namespace std;class Demo {int i;public:int geti () const (return i; // здесь все правильно}void seti{int x) const {i = x; // Ошибка! ! !int main ( )Demo ob;ob.seti(1900) ;cout « ob.getif);return 0;Данная программа не будет компилироваться, поскольку функция-член seti()объявлена постоянной, что означает невозможность изменения вызывающегоее объекта. Таким образом, попытка изменения функцией переменной i ведет к ошибке. С другой стороны, поскольку функция geti() не меняет переменной i, она совершенно правильна.2.
Чтобы допустить изменение избранных членов класса постоянной функциейчленом, они задаются модифицируемыми. Ниже представлен пример.// Пример задания модифицируемого члена класса#include <iostream>using namespace std;class Demo {mutable int i;int j ;public:int geti() const {return i; // здесь все правильно}void seti(int x) const {i = x; // теперь все правильноГлава13.Пространстваименидругиетемы_403/* Если убрать комментарии вокруг этой функции, то программакомпилироваться не Судетvoid setj (int x) const {j = x; // здесь прежняя ошибка}*/int main (){Demo ob;ob.seti(1900) ;cout « ob.geti () ;return 0;IЗдесь переменная i задана модифицируемой, поэтому ее может изменитьфункция-член seti().
Тем не менее, поскольку переменная] по-прежнему остается не модифицируемой, постоянная функция-член seti() не может изменить ее значение.УпраАне!_^В следующей программе сделана попытка создать простой таймер для измерения временных интервалов. По истечении каждого такого интервала таймер должен подавать звуковой сигнал. К сожалению, в том виде, в которомпрограмма представлена, она компилироваться не будет. Найдите и исправьте ошибку.// В этой программе имеется ошибка#include <iostream>using namespace std;class CountDown {int incr;int target;int current;public:CountDown(int delay, int i ~ 1)target = delay;incr = i;current = 0;}bool counting{) const {current += incr;{404Самоучитель C++if(current >= target) {cout « " \ a " ;return false;}cout « current «" ";return true;int main{){CountDown ob(100, 2);while(ob.counting());return 0;2.
Может ли постоянная функция-член вызвать непостоянную функцию? Еслинет, то почему?13.5. Заключительный обзорконструкторовХотя тема конструкторов в этой книге уже обсуждалась, некоторые аспектыих применения остались нераскрытыми. Рассмотрим следующую программу:^include <iostream>using namespace std;class myclass {int a;public:myclass(int x) { a = x; }int geta() { return a; )int main{)myclass ob(4);cout « ob.geta(};treturn 0;Глава13.Пространстваименидругиетемы_405Здесь у конструктора класса myclass имеется один параметр. Обратите особое внимание на то, как в функции main О объявлен объект ob. Значение 4,заданное в скобках сразу за объектом ob, — это аргумент, который передается параметру х конструктора myclassQ и с помощью которого инициализируется переменная а.
Именно такая форма инициализации использовалась впримерах программ, начиная с первых глав этой книги. Однако это неединственный способ инициализации. Рассмотрим, к примеру, следующуюинструкцию:myclass ob = 4; // эта инструкция автоматически преобразуется// в инструкцию myclass ob{4);Как показано в комментариях, эта форма инициализации автоматическипреобразуется в вызов конструктора myclassQ со значением 4 в качестве аргумента.
Таким образом, предыдущая инструкция обрабатывается компилятором так, как будто на ее месте находится инструкция:myclass o b ( 4 ) ;Как правило, всегда, когда у конструктора имеется только один аргумент,можно использовать любую из представленных выше двух форм инициализации объекта. Смысл второй формы инициализации в том, что для конструктора с одним аргументом она позволяет организовать неявное преобразование типа этого аргумента в тип класса, к которому относится конструктор.Неявное преобразование можно запретить с помощью спецификатора explicit (явный). Спецификатор explicit применим только к конструкторам. Дляконструкторов, заданных со спецификатором explicit, допустим толькообычный синтаксис.
Автоматического преобразования для таких конструкторов не выполняется. Например, если в предыдущем примере конструкторкласса myclass объявить со спецификатором explicit, то для такого конструктора автоматического преобразования поддерживаться не будет. В представленном ниже классе конструктор myclass() объявлен со спецификаторомexplicit.^include <iostream>using namespace std;class myclass {int a;public:explicit myclass (int x) { a = x; }int geta() { return a; }Для такого класса допустима только одна форма конструкторов:myclass o b ( 4 ) ;406Самоучитель C++1.
В классе может быть более одного преобразующего конструктора. Например,рассмотрим следующую версию класса myclass.#include <iostream>#include <cstdlit>>using namespace std;/class myclass {int a;public:myclass (int x) { a = x; }myclass (char *str) { a = atoi(str); }int geta() { return a; }i•int main(){// преобразование в вызов конструктора myclass ob{4)myclass obi = 4;// преобразование в вызов конструктора myclass ob("123"3myclass ob2 = "123";cout « "obi: " « obl.getaO « endl;cout « "ob2: " « ob2.geta{) « endl;return 0;Поскольку типы аргументов обоих конструкторов различны (как это и должно быть) каждая инструкция инициализации автоматически преобразуется всоответствующий вызов конструктора.2.
Автоматическое преобразование на основе типа первого аргумента конструктора в вызов самого конструктора имеет интересное применение. Например,для класса myclass из примера 1, чтобы присвоить объектам оЫ и оЬ2 новыезначения, функция main() выполняет преобразования из типа int в типchar *.linclude <iostream>^include <cstdlib>using namespace std;class myclass {int a;public:myclass (int x) { a = x; }myclass (char *str) { a = a t o i ( s t r ) ; }Глава 13.
Пространства имен и другие темыint g e t a ( )407{ return a; }};int main(}{// преобразование в вызов конструктора myclass o b ( 4 )myclass obi = 4;// преобразование в вызов конструктора myclass ob("123")myclass ob2 = "123";cout « "obi: " « obl.getaO « endl;cout « "ob2: " « ob2.geta{) « endl;/* использование автоматического преобразования для присваиванияновых значений*/// преобразование в вызов конструктора myclass ob("1776")myclass obi = "1776";// преобразование в вызов конструктора myclass ob(2001)myclass obi = 2001;cout « "оЫ: " « obl.getaO « endl;cout « "ob2: " « ob2.geta(} « endl;return 0;3.
Чтобы запретить показанные в предыдущих примерах преобразования, дляконструкторов можно задать спецификатор explicit:^include <iostream>ftinclude <cstdlib>using namespace std;class myclass (int a;public:explicit myclass(int x) { a = x; }.explicit myclass(char *str) { a = atoi(str); }int geta() { return a; }int mainO{// преобразование в вызов конструктора myclass ob(4)myclass obi = 4;.// преобразование в вызов конструктора myclass ob{"123"}myclass obi - "123";408Самоучитель C++cout « "obi: " « o b l .