Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 Mб
Скачать

1.4.5 Преобразования типов

      Определяемые пользователем преобразования типа, например, такие, как преобразование числа с плавающей точкой в комплексное, которое необходимо для конструктора complex(double), оказались очень полезными в С++. Программист может задавать эти преобразования явно, а может полагаться на транслятор, который выполняет их неявно в том случае, когда они необходимы и однозначны:       complex a = complex ( 1 );       complex b = 1; // неявно: 1 -> complex ( 1 )       a = b + complex ( 2 );       a = b + 2; // неявно: 2 -> complex ( 2)       Преобразования типов нужны в С++ потому, что арифметические операции со смешанными типами являются нормой для языков, используемых в числовых задачах. Кроме того, большая часть пользовательских типов, используемых для "вычислений" (например, матрицы, строки, машинные адреса) допускает естественное преобразование в другие типы (или из других типов).       Преобразования типов способствуют более естественной записи программы:       complex a = 2;       complex b = a + 2; // это означает: operator + ( a, complex ( 2 ))       b = 2 + a; // это означает: operator + ( complex ( 2 ), a )       В обоих случаях для выполнения операции "+" нужна только одна функция, а ее параметры единообразно трактуются системой типов языка. Более того, класс complex описывается так, что для естественного и беспрепятственного обобщения понятия числа нет необходимости что-то изменять для целых чисел.

1.4.6 Множественные реализации

      Основные средства, поддерживающие объектно-ориентированное программирование, а именно: производные классы и виртуальные функции,- можно использовать и для поддержки абстракции данных, если допустить несколько реализаций одного типа. Вернемся к примеру со стеком:       template < class T >       class stack       {       public:       virtual void push ( T ) = 0; // чистая виртуальная функция       virtual T pop () = 0; // чистая виртуальная функция       };       Обозначение =0 показывает, что для виртуальной функции не требуется никакого определения, а класс stack является абстрактным, т.е. он может использоваться только как базовый класс. Поэтому стеки можно использовать, но не создавать:       class cat { /* ... */ };       stack < cat > s; // ошибка: стек - абстрактный класс       void some_function ( stack <cat> & s, cat kitty ) // нормально       {       s.push ( kitty );       cat c2 = s.pop ();       // ...       }       Поскольку интерфейс стека ничего не сообщает о его представлении, от пользователей стека полностью скрыты детали его реализации.       Можно предложить несколько различных реализаций стека. Например, стек может быть массивом:       template < class T >       class astack : public stack < T >       {       // истинное представление объекта типа стек       // в данном случае - это массив       // ...       public:       astack ( int size );       ~astack ();       void push ( T );       T pop ();       };       Можно реализовать стек как связанный список:       template < class T >       class lstack : public stack < T >       {       // ...       };       Теперь можно создавать и использовать стеки:       void g ()       {       lstack < cat > s1 ( 100 );       astack < cat > s2 ( 100 );       cat Ginger;       cat Snowball;       some_function ( s1, Ginger );       some_function ( s2, Snowball );       }       О том, как представлять стеки разных видов, должен беспокоиться только тот, кто их создает (т.е. функция g()), а пользователь стека (т.е. автор функции some_function()) полностью огражден от деталей их реализации. Платой за подобную гибкость является то, что все операции над стеками должны быть виртуальными функциями.