ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3203

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Введение указателей в С обеспечило успех этого языка программирования. Работа

суказателями— очень мощная возможность. Старые программисты на машинных язы-

ибыли несказанно рады, получив в свои руки такой инструмент. В С++ возможности работы с указателями унаследованы без изменений от С.

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

Проблемы, связанные с указателями, трудно локализовать. Зачастую программа с не­ корректным указателем при каждом новом запуске ведет себя по-новому.

С# решает проблемы, связанные с указателями, очень просто — он попросту устраня­ ет их из языка. Используемые вместо них ссылки безопасны с точки зрения типов и не могут быть применены так, чтобы это приводило к краху программы.

Если вы сравните новые обобщенные возможности С# (см. главу 15, "Обобщенное программирование") с шаблонами С++, то обнаружите высокую степень схожести их (интаксиса. Однако хотя оба средства имеют общее предназначение, такое сходство яв­ ляется чисто внешним.

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

Наибольшее различие между обобщенными классами и шаблонами состоит пом, что обобщенные классы работают с несколькими языками, включая Visual Basic, С++ и другие языки .NET, в том числе С#. Шаблоны же используются только (рамках С++.

Что же лучше? Шаблоны более мощны — и более сложны, как и множество других идей в С++, но и больше подвержены ошибкам. Обобщенные классы проще в испольювании и реже приводят к ошибкам в программах.

Конечно, это всего лишь некоторые тезисы бурной дискуссии. Существенно большую информацию можно найти в блоге Брендона Брея (Brandon Bray) по адресу weblogs . a s p . n e t / b r a n b r a y / a r c h i v e / 2 0 0 3 / 1 1 / 1 9 / 5 1 0 2 3 . a s p x .

Г л а в а 17, Десять основных отличий С# и С++

387


С++ обеспечивает строгую проверку типов — и это хорошо. Он выполняет эот заставляя объявлять функции и классы в так называемых включаемых файлах, кото рые затем используются модулями. Однако правильное перечисление в правили порядке всех включаемых файлов для компиляции вашего модуля — задача не из простых.

С# избегает бессмысленной работы. Он ищет и находит определения всех классов Если вы вызываете класс S t u d e n t , С# находит определение этого класса, чтобы убе диться, что вы используете его корректно.

Сначала казалось, что конструкторы приносят большую пользу. Наличие специальн ной функции, гарантирующей корректную настройку всех членов-данных... Отличная идея! Единственная проблема в том, что приходится добавлять в каждый написанный класс тривиальный конструктор по умолчанию. Рассмотрим следующий пример:

p u b l i c c l a s s

A c c o u n t

{

 

 

p r i v a t e

d o u b l e b a l a n c e ;

p r i v a t e

i n t

n u m C h e c k s P r o c e s s e d ;

p r i v a t e

CheckBook c h e c k B o o k ;

p u b l i c A c c o u n t ( )

{

b a l a n c e = 0 . 0 ;

n u m C h e c k s P r o c e s s e d = 0;

c h e c k B o o k = new C h e c k B o o k ( ) ;

}

}

Почему же нельзя инициализировать члены-данные непосредственно и позволить языку программирования самому сгенерировать конструктор? С++ отвечает, почему; C# отвечает — почему нет? С# позволяет избавиться от ненужных конструкторов с помощью непосредственной инициализации:

p u b l i c c l a s s A c c o u n t

{

p r i v a t e d o u b l e b a l a n c e = 0 . 0 ;

p r i v a t e

i n t n u m C h e c k s P r o c e s s e d

p r i v a t e

CheckBook

c h e c k B o o k =

// Больше э т о не

н а д о д е л а т ь

=0 ;

new C h e c k B o o k ( ) ;

вк о н с т р у к т о р е

}

Более того, если все, что нужно — это соответствующая версия нуля для определенного типа, как в случае первых двух членов, С# примет необходимые меры автоматиче- ски, как минимум для членов-данных классов. Если вы хотите нечто, отличное от нуля, добавьте вашу собственную инициализацию к объявлению членов-данных. (Однако сле­ дует всегда инициализировать локальные переменные в функциях.)

388 Часть VI. Великолепные десятки


С++ очень политкорректен. Он и шагу не ступит ни на одном компьютере без того, чтобы определить требования к диапазону значений и размеру конкретных типов. Он указывает, что i n t имеет такой-то размер, a l o n g — больший. Все это приводит к по­ явлению ошибок при переносе программ с одного типа процессора на другой.

С# не заботится о таких мелочах. Он прямо говорит — i n t имеет 32 бит, a l o n g — 64 бит, и так должно быть.

С++ позволяет одному классу наследовать более чем один базовый класс. Например, класс S l e e p e r S o f а (диван-кровать) может наследовать классы Bed (кровать) и S o f a (диван). Наследование от двух классов звучит неплохо, и это и в самом деле бывает очень полезно. Проблема только в том, что множественное наследование может приво­ дить к некоторым трудно обнаружимым ошибкам.

С# не рискует и снижает количество возможных ошибок, запрещая множественное наследование. Однако в С# имеется возможность, которая в ряде ситуаций может заме­ нить множественное наследование, а именно — интерфейсы.

Когда программисты продираются сквозь кошмар множественного наследования и 90% времени проводят в отладчике, зачастую выясняется, что второй базовый класс нужен только для того, чтобы описать подкласс. Например, обычный класс может наследовать абстрактный класс P e r s i s t a b l e с абстрактными методами r e a d () и w r i t e ( ) . Это за­ ставляет подкласс реализовать методы r e a d () и w r i t e () и объявить всему миру, что эти методы доступны для использования.

После этого программисты осознают, что того же можно добиться существенно более легкими средствами — посредством интерфейса. Класс, который реализует интерфейс наподобие приведенного ниже, тоже обещает предоставить методы r e a d () и w r i t e ( ) : interface I P e r s i s t a b l e

{

void r e a d ( ) ; void write () ;

}

Так вы избегаете опасностей множественного наследования С++ и получаете желае­ мый результат.

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

Глава 17. Десять основных ОТЛИЧИЙ С# И С++

389


ческим представлениям о реальном мире. Единственная проблема заключается в том, что любой язык программирования должен обеспечить еще и простейшие типы переменных для хранения, например, целых чисел или чисел с плавающей точкой. Это определяет необходимость системы преобразования типов. Объекты классов и переменные типов значений находятся по разные стороны баррикад, хотя и участвуют вместе в одних и тех же программах. Программист вынужден все время помнить о том, что это разные вещей по своей природе.

С# разрушает эту баррикаду, отделяющую типы-значения от классов. Для каждом типа-значения имеется соответствующий класс, именуемый структурой (вы можете так же писать и собственные структуры; см. главу 14, "Интерфейсы и структуры"). Эти структуры могут легко объединяться с классами, позволяя программисту писать исход­ ный текст наподобие следующего:

M y C l a s s

m y O b j e c t =

new

M y C l a s s О,-

// Вывод

" m y O b j e c t "

в

с т р о к о в о м формате

C o n s o l e . W r i t e L i n e ( m y O b j e c t . T o S t r i n g ( ) ) ;

i n t

i =

5 ;

//

Вывод

i n t в с т р о к о в о м формате

C o n s o l e . W r i t e L i n e ( i . T o S t r i n g ( ) ) ;

//

Вывод

к о н с т а н т ы 5 в с т р о к о в о м формате

C o n s o l e . W r i t e L i n e ( 5 . T o S t r i n g ( ) ) ;

Можно вызвать один и тот же метод не только для переменной i n t и объекта класса MyClass, но даже для константы наподобие 5. Такое вавилонское смешение типов-

одна из мощных возможностей С#.

390

Часть VI. Великолепные десятки


Часть VII

Дополнительные главы