Главная страница
Навигация по странице:

  • 13.1.1. Данные-члены

  • 13.1.2. Функции-члены

  • 13.1.4. Друзья

  • 13.1.5. Объявление и определение класса

  • Язык программирования C++. Вводный курс. С для начинающих


    Скачать 5.41 Mb.
    НазваниеС для начинающих
    Дата24.08.2022
    Размер5.41 Mb.
    Формат файлаpdf
    Имя файлаЯзык программирования C++. Вводный курс.pdf
    ТипДокументы
    #652350
    страница56 из 93
    1   ...   52   53   54   55   56   57   58   59   ...   93
    582
    ilist.unique( EvenPair() ); удаляет соседние элементы, если второй элемент без остатка делится на первый.
    Эти операции, являющиеся членами класса, следует предпочесть соответствующим обобщенным алгоритмам при работе со списками. Остальные обобщенные алгоритмы, такие, как find(), transform(), for_each() и т.д., работают со списками так же эффективно, как и с другими контейнерами (еще раз напомним, что подробно все алгоритмы рассматриваются в Приложении).
    Упражнение 12.8
    Измените программу из раздела 12.2, используя список вместо вектора. class EvenPair { public: bool operator()( int val1, val2 )
    { return ! (val2 % val1 ); }
    };

    С++ для начинающих
    583
    Часть IV
    Объектное программирование
    В части 4 мы сосредоточимся на объектном программировании, т.е. на применении классов C++ для определения новых типов, манипулировать которыми так же просто, как и встроенными. Создавая новые типы для описания предметной области, C++ помогает программисту писать более легкие для понимания приложения. Классы позволяют отделить детали, касающиеся реализации нового типа, от определения интерфейса и операций, предоставляемых пользователю. При этом уделяется меньше внимания мелочам, из-за чего программирование становится таким утомительным занятием.
    Значимые для приложения типы можно реализовать всего один раз, после чего использовать повторно. Средства, обеспечивающие инкапсуляцию данных и функций, необходимых для реализации типа, помогают значительно упростить последующее сопровождение и развитие приложения.
    В главе 13 мы рассмотрим общий механизм классов: порядок их определения, концепцию
    сокрытия информации (т.е. отделение открытого интерфейса от закрытой реализации), способы определения и манипулирования объектами класса, область видимости, вложенные классы и классы как члены пространства имен.
    В главе 14 изучаются предоставляемые C++ средства инициализации и уничтожения объектов класса, а также присваивания им значений путем применения таких специальных функций-членов класса, как конструкторы, деструкторы и копирующие
    конструкторы. Мы рассмотрим вопрос о почленной инициализации и копировании, когда объект класса инициализируется или ему присваивается значение другого объекта того же класса.
    В главе 15 мы расскажем о перегрузке операторов, которая позволяет использовать операнды типа класса со встроенными операторами, описанными в главе 4. Таким образом, работа с объектами типа класса может быть сделана столь же понятной, как и работа со встроенными типами. В начале главы 15 представлены общие концепции и соображения, касающиеся проектирования перегрузки операторов, а затем рассмотрены конкретные операторы, такие, как присваивание, взятие индекса, вызов, а также специфичные для классов операторы new и delete. Иногда необходимо объявить перегруженный оператор, как друга класса, наделив его специальными правами доступа, в данной главе объясняется, зачем это нужно. Здесь же представлен еще один специальный вид функций-членов – конвертеры, которые позволяют программисту определить стандартные преобразования.
    Конвертеры неявно применяются компилятором, когда объекты класса используются в качестве фактических аргументов функции или операндов встроенного либо перегруженного оператора. Завершается глава изложением правил разрешения перегрузки функций с учетом аргументов типа класса, функций-членов и перегруженных операторов.
    Тема главы 16 – шаблоны классов. Шаблон – это предписание для создания класса, в котором один или несколько типов параметризованы. Например, vector может быть параметризован типом элементов, хранящихся в нем, а buffer – типом элементов в буфере или его размером. В этой главе объясняется, как определить и конкретизировать шаблон. Поддержка классов в C++ теперь рассматривается иначе – в свете наличия шаблонов, и снова обсуждаются функции-члены, объявления друзей и вложенные типы.
    Здесь мы еще раз вернемся к модели компиляции шаблонов, описанной в главе 10, чтобы показать, какое влияние оказывают на нее шаблоны классов.

    С++ для начинающих
    584
    13
    13.
    Классы
    Механизм классов в C++ позволяет пользователям определять собственные типы данных. По этой причине их часто называют пользовательскими типами. Класс может наделять дополнительной функциональностью уже существующий тип. Так, например, IntArray, введенный в главе 2, предоставляет больше возможностей, чем тип “массив int”. С помощью классов можно создавать абсолютно новые типы, например Screen (экран) или Account (расчетный счет). Как правило, классы используются для абстракций, не отражаемых встроенными типами адекватно.
    В этой главе мы узнаем, как определять типы и использовать объекты классов; увидим, что определение класса вводит как данные-члены, описывающие его, так и функции-члены, составляющие набор операций, применимых к объектам класса.
    Мы покажем, как можно обеспечить сокрытие информации, объявив внутреннее представление и реализацию закрытыми, но открыв операции над объектами.
    Говорят, что закрытое внутреннее представление инкапсулировано, а открытую часть класса называют его интерфейсом.
    Далее в этой главе мы познакомимся с особым видом членов класса – статическими членами. Мы расскажем также, как можно использовать указатели на члены и функции-члены класса, и рассмотрим объединения, представляющие собой специализированный вид класса для хранения объектов разных типов в одной области памяти. Завершается глава обсуждением области видимости класса и описанием правил разрешения имен в этой области; затрагиваются такие понятия, как вложенные классы, классы-члены пространства имен и локальные классы.
    13.1.
    Определение класса
    Определение класса состоит из двух частей: заголовка, включающего ключевое слово class
    , за которым следует имя класса, и тела, заключенного в фигурные скобки. После такого определения должны стоять точка с запятой или список объявлений: class Screen { /* ... */ } myScreen, yourScreen;
    Внутри тела объявляются данные-члены и функции-члены и указываются уровни доступа к ним. Таким образом, тело класса определяет список его членов.
    Каждое определение вводит новый тип данных. Даже если два класса имеют одинаковые списки членов, они все равно считаются разными типами: class Screen { /* ... */ };

    С++ для начинающих
    585
    Second obj2 = obj1; // ошибка: obj1 и obj2 имеют разные типы
    Тело класса определяет отдельную область видимости. Объявление членов внутри тела помещает их имена в область видимости класса. Наличие в двух разных классах членов с одинаковыми именами – не ошибка, эти имена относятся к разным объектам. (Подробнее об областях видимости классов мы поговорим в разделе 13.9.)
    После того как тип класса определен, на него можно ссылаться двумя способами:

    написать ключевое слово class, а после него – имя класса. В предыдущем примере объект obj1 класса First объявлен именно таким образом;

    указать только имя класса. Так объявлен объект obj2 класса Second из приведенного примера.
    Оба способа сослаться на тип класса эквивалентны. Первый заимствован из языка C и остается корректным методом задания типа класса; второй способ введен в C++ для упрощения объявлений.
    13.1.1.
    Данные-члены
    Данные-члены класса объявляются так же, как переменные. Например, у класса Screen могут быть следующие данные-члены:
    };
    Поскольку мы решили использовать строки для внутреннего представления объекта класса Screen, то член _screen имеет тип string. Член _cursor – это смещение в строке, он применяется для указания текущей позиции на экране. Для него использован переносимый тип string::size_type. (Тип size_type рассматривался в разделе 6.8.)
    Необязательно объявлять два члена типа short по отдельности. Вот объявление класса
    Screen
    , эквивалентное приведенному выше: class First { int memi; double memd;
    }; class Second { int memi; double memd;
    }; class First obj1;
    #include class Screen { string _screen; // string( _height * _width ) string::size_type _cursor; // текущее положение на экране short _height; // число строк short _width; // число колонок

    С++ для начинающих
    586
    };
    Член класса может иметь любой тип:
    };
    Описанные данные-члены называются нестатическими. Класс может иметь также и
    статические данные-члены. (У них есть особые свойства, которые мы рассмотрим в разделе 13.5.)
    Объявления данных-членов очень похожи на объявления переменных в области видимости блока или пространства имен. Однако их, за исключением статических членов, нельзя явно инициализировать в теле класса:
    };
    Данные-члены класса инициализируются с помощью конструктора класса. (Мы рассказывали о конструкторах в разделе 2.3; более подробно они рассматриваются в главе 14.)
    13.1.2.
    Функции-члены
    Пользователям, по-видимому, понадобится широкий набор операций над объектами типа
    Screen
    : возможность перемещать курсор, проверять и устанавливать области экрана и рассчитывать его реальные размеры во время выполнения, а также копировать один объект в другой. Все эти операции можно реализовать с помощью функций-членов.
    Функции-члены класса объявляются в его теле. Это объявление выглядит точно так же, как объявление функции в области видимости пространства имен. (Напомним, что глобальная область видимости – это тоже область видимости пространства имен.
    Глобальные функции рассматривались в разделе 8.2, а пространства имен – в разделе
    8.5.) Например: class Screen {
    /*
    * _ screen адресует строку размером _height * _width
    * _cursor указывает текущую позицию на экране
    * _height и _width - соответственно число строк и колонок
    */ string _screen; string::size_type _cursor; short _height, _width; class StackScreen { int topStack; void (*handler)(); // указатель на функцию vector stack; // вектор классов class First { int memi = 0; // ошибка double memd = 0.0; // ошибка

    С++ для начинающих
    587
    };
    Определение функции-члена также можно поместить внутрь тела класса:
    }; home()
    перемещает курсор в левый верхний угол экрана; get() возвращает символ, находящийся в текущей позиции курсора.
    Функции-члены отличаются от обычных функций следующим:

    функция-член объявлена в области видимости своего класса, следовательно, ее имя не видно за пределами этой области. К функции-члену можно обратиться с помощью одного из операторов доступа к членам – точки (.) или стрелки (->): myScreen.home();
    (в разделе 13.9 область видимости класса обсуждается более детально);

    функции-члены имеют право доступа как к открытым, так и к закрытым членам класса, тогда как обычным функциям доступны лишь открытые. Конечно, функции- члены одного класса, как правило, не имеют доступа к данным-членам другого класса.
    Функция-член может быть перегруженной (перегруженные функции рассматриваются в главе 9). Однако она способна перегружать лишь другую функцию-член своего класса.
    По отношению к функциям, объявленным в других классах или пространствах имен, функция-член находится в отдельной области видимости и, следовательно, не может перегружать их. Например, объявление get(int, int) перегружает лишь get() из того же класса Screen: class Screen { public: void home(); void move( int, int ); char get(); char get( int, int ); void checkRange( int, int );
    // ... class Screen { public:
    // определения функций home() и get() void home() { _cursor = 0; } char get() { return _screen[_cursor]; }
    // ... ptrScreen->home();

    С++ для начинающих
    588
    };
    (Подробнее мы остановимся на функциях-членах класса в разделе 13.3.)
    13.1.3.
    Доступ к членам
    Часто бывает так, что внутреннее представление типа класса изменяется в последующих версиях программы. Допустим, опрос пользователей нашего класса Screen показал, что для его объектов всегда задается размер экрана 80
    ×
    24. В таком случае было бы желательно заменить внутреннее представление экрана менее гибким, но более эффективным:
    };
    Прежняя реализация функций-членов (то, как они манипулируют данными-членами класса) больше не годится, ее нужно переписать. Но это не означает, что должен измениться и интерфейс функций-членов (список формальных параметров и тип возвращаемого значения).
    Если бы данные-члены класса Screen были открыты и доступны любой функции внутри программы, как отразилось бы на пользователях изменение внутреннего представления этого класса?

    все функции, которые напрямую обращались к данным-членам старого представления, перестали бы работать. Следовательно, пришлось бы отыскивать и изменять соответствующие части кода;

    так как интерфейс не изменился, то коды, манипулировавшие объектами класса
    Screen только через функции-члены, не пришлось бы модифицировать. Но поскольку сами функции-члены все же изменились, программу пришлось бы откомпилировать заново.
    Сокрытие информации – это формальный механизм, предотвращающий прямой доступ к внутреннему представлению типа класса из функций программы. Ограничение доступа к членам задается с помощью секций тела класса, помеченных ключевыми словами public
    , private и protected – спецификаторами доступа. Члены, объявленные в секции public, называются открытыми, а объявленные в секциях private и protected соответственно закрытыми или защищенными. class Screen { public:
    // объявления перегруженных функций-членов get() char get() { return _screen[_cursor]; } char get( int, int );
    // ... class Screen { public:
    // функции-члены private:
    // инициализация статических членов (см. 13.5) static const int _height = 24; static const int _width = 80; string _screen; string::size_type _cursor;

    С++ для начинающих
    589

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

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

    защищенный член ведет себя как открытый по отношению к производному классу и как закрытый по отношению к остальной части программы. (В главе 2 мы видели пример использования защищенных членов в классе IntArray. Детально они рассматриваются в главе 17, где вводится понятие наследования.)
    В следующем определении класса Screen указаны секции public и private:
    };
    Согласно принятому соглашению, сначала объявляются открытые члены класса.
    (Обсуждение того, почему в старых программах C++ сначала шли закрытые члены и почему этот стиль еще кое-где сохранился, см. в книге [LIPPMAN96a].) В теле класса может быть несколько секций public, protected и private. Каждая секция продолжается либо до метки следующей секции, либо до закрывающей фигурной скобки.
    Если спецификатор доступа не указан, то секция, непосредственно следующая за открывающей скобкой, по умолчанию считается private.
    13.1.4.
    Друзья
    Иногда удобно разрешить некоторым функциям доступ к закрытым членам класса.
    Механизм друзей позволяет классу разрешать доступ к своим неоткрытым членам.
    Объявление друга начинается с ключевого слова friend и может встречаться только внутри определения класса. Так как друзья не являются членами класса, то не имеет значения, в какой секции они объявлены. В примере ниже мы сгруппировали все подобные объявления сразу после заголовка класса:
    }; class Screen { public: void home() { _cursor = 0; } char get() { return _screen[_cursor]; } char get( int, int ); void move( int, int );
    // ... private: string _screen; string::size_type _cursor; short _height, _width; class Screen { friend istream& operator>>( istream&, Screen& ); friend ostream& operator<<( ostream&, const Screen& ); public:
    // ... оставшаяся часть класса Screen

    С++ для начинающих
    590
    Операторы ввода и вывода теперь могут напрямую обращаться к закрытым членам класса Screen. Простая реализация оператора вывода выглядит следующим образом:
    }
    Другом может быть функция из пространства имен, функция-член другого класса или даже целый класс. В последнем случае всем его функциям-членам предоставляется доступ к неоткрытым членам класса, объявляющего дружественные отношения. (В разделе 15.2 друзья обсуждаются более подробно.)
    13.1.5.
    Объявление и определение класса
    О классе говорят, что он определен, как только встретилась скобка, закрывающая его тело. После этого становятся известными все члены класса, а следовательно, и его размер.
    Можно объявить класс, не определяя его. Например: class Screen; // объявление класса Screen
    Это объявление вводит в программу имя Screen и указывает, что оно относится к типу класса.
    Тип объявленного, но еще не определенного класса допустимо использовать весьма ограниченно. Нельзя определять объект типа класса, если сам класс еще не определен, поскольку размер класса в этом момент неизвестен и компилятор не знает, сколько памяти отвести под объект.
    Однако указатель или ссылку на объект такого класса объявлять можно, так как они имеют фиксированный размер, не зависящий от типа. Но, поскольку размеры класса и его членов неизвестны, применять оператор разыменования (*) к такому указателю, а также использовать указатель или ссылку для обращения к члену не разрешается, пока класс не будет полностью определен.
    Член некоторого класса можно объявить принадлежащим к типу какого-либо класса только тогда, когда компилятор уже видел определение этого класса. До этого объявляются лишь члены, являющиеся указателями или ссылками на такой тип. Ниже приведено определение StackScreen, один из членов которого служит указателем на
    Screen
    , который объявлен, но еще не определен:
    #include ostream& operator<<( ostream& os, const Screen& s )
    {
    // правильно: можно обращаться к _height, _width и _screen os << "<" << s._height
    << "," << s._width << ">"; os << s._screen; return os;

    С++ для начинающих
    1   ...   52   53   54   55   56   57   58   59   ...   93


    написать администратору сайта