Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 113
Текст из файла (страница 113)
Если хотите проверить зто, попробуйте выполнить предло:кенный пример в "родном" С++. Вы обнаружите, что он работает, как ожидается. 426 Глава 13 Вся прелесть возможности объявления методов ргтчаге о1геиа1 состоит в том. что не придется беспокоиться о производных классах. которые неправильно используют класс вазе. Например, может быть, необходимо, чтобы они не вызывали реализации виртуального метода базового класса. Отлично, для этого нужно просто объявить его как ргтчасе чагсоа1.Фактически, делать метод ргосессес( чагспа1 следует только тогда, когдаимеется веская причина,по которой базовому классу может понадобиться вызывать его. В этом случае потребуется строго документировать.
в какой точке переопределение должно вызывать базовую реализацию. Многие думают, что лишь потому, что метод объявлен как ргтласе, он не может быть переопределен. Но, строго говоря, ограниченная доступность не делает его невидимым или переопределенным. А теперь посмотрим, почему это средство было отключено в версии С№ из .)ЧЕТ 1.1. Это было загадкой до тех пор, пока Брендон Брай (Вгапбоп Вгау] из команды разработчиков М(сгозой т(зца) С++ не объяснил все четко. Возможность наследования через границы доступности превращала это средство в определенного рода брешь в безопасности. В "родном" С++ это никогда не было проблемой.
Если метод ргаоасе чтгспа1 можно было бы переопределить, то же самое стало бы возможным и для метода 1псегпа1 чагсиа1. И здесь возникает проблема. Это позволило бы переопределять поведение метода 1псегпа1 чагсоа1 в некотором произвольном классе в определенной сборке, что повлекло бы за собой брешь в безопасности. В связи с этим было принято компромиссное решение, начиная с версии .)()ЕТ 1.
1, отключить данное средство. Аналогичное исправление было внесено и в С++/СЫ. Хотя "родные" классы С++ могут аффективно использовать средство методов рг1часе ттгсоа1, гег-классы С++ делать этого не могут. Причина, конечно же, в том, что геГ-классы С++ представляют типы геГ из .(ЧЕТ, которые не могут быть унаследованы через границы сборок.
Вот в чем все дело. Является ли ОЪ~ее~ клонируемым? Как известно, объекты в С№ и СЬК находятся в куче и доступны через ссылки. Присваивание одной переменной Оь1есс другой на самом деле не приводит к созданию копии: ОЬ1есг оЬ1 = пеэ ОЬ~есе О ОЬ1есг оЬ1Сору = оЬ1~ После выполнения этого кода ОЬ1 Сору не ссылается на копию ОЬВ: вместо этого будут получены две ссылки на один и тот же экземпляр ОЬб ест. Однако иногда имеет смысл создать копию объекта. Для этой цели в стандартной библиотеке предусмотрен интерфейс 1С1опеаЫе. Если объект реализует этот интерфейс, то можно говорить, что он поддерживает возможность создания копий самого себя. Другими словами, от него требуется, чтобы он мог служить прототипом для создания новых экземпляров объектов.
Объекты подобного рода могут участвовать в шаблоне фабрики прототипов. Прежде чем двигаться дальше, взглянем, как выглядит интерфейс 1С1опеаЬ1е: роЬ11с тпгегГасе 1С1опеаЫе ( оЬ1есс С1опе(); ) Как видите. в интерфейсе объявлен только один метод — С1опе, который возвращает объектную ссылку.
Предполагается, что эта ссылка указывает на копию. Все, что потребуется сделать — это вернуть копию объекта, правильно.г Но не следует торопиться. Как видите, с определением интерфейса связана нетривиальная проблема. В документации по интерфейсу не указано, какой должна быть возвращенная копия; глубокой Б поисках канонических форм СЗ 427 (плеер) или поверхностной (вйайочк). Фактически это оставляется иа усмотрение проектировщика класса.
Разница между поверхностной и глубокой копией существенна только в том случае, если объект содержит ссылки иа другие объенты. ° Поверхностная копия объекта — это копия, в которой содержащиеся объектные ссылки указывают иа те же объекты, что и в прототипе. ° Пкубокая копия — это копия, в которой все содержащиеся объекты также копируются.
В глубокой копии дерево содержащихся объектов проходится сверху вниз, и по пути осуществляется копирование всех объектов. Поэтому результат глубокого копирования ие разделяет никаких общих объектов с прототипом. Этого достаточно, чтобы свести с ума хорошего разработчика программного обеспечения. Выглядит логичным, что если действительно нужно создать копию объекта, то единственно правильным путем должно считаться глубокое копирование. Отлично! С этого момента под клоном будет пониматься именно глубокая копия.
Для того чтобы объект позволял выполнять нлоиироваиие самого себя, следует помнить, что клон — это глубоная нопия, поэтому во всех содержащихся объектах должны быть предусмотрены средства глубокого копирования самих себя. Из-за этого требования возникает одна проблема. Гарантировать глубокое копирование ие удастся, если объект содержит ссылки иа объекты. которые ие поддерживают глубокое копирование. Вот почему мы страдаем от документации интерфейса 1С1опеаЬ1е и недостатка в ией спецификации семантики копирования.
Плюс, и это важно, такой недостаток спецификации заставляет более четно документировать реализацию 1С1опеаЬ1е в любом объенте, реализующем этот интерфейс. чтобы потребители знали, поддерживает объект поверхностную или глубокую копию. Рассмотрим варианты реализации интерфейса 1С1опеаЬ1е в объектах.
Если объект содержит в себе только типы значений, такие как Бпг, 1опд или значения, основанные иа определениях структур. не имеющих в себе ссылочиых типов, то для реализации метода С1опе применяется сокращенный способ в форме вызова ОЬ) еск. МевЬегиавеС1опе, как поназаио ниже: цяапч Буясепи Рцвтас яеа1ек) с1авя О1аепя1опя: 1С1опеапае ( рцваас Оатепяаопв( 1опо и1к(СЬ, 1опч Пеачпк ) ( кп1в.и1к(КЬ = иаоевк кп1я.петопк = Ье1оис; ) // Реализация 1С1опеаЬ1е. рцЬ11с оЬ)еск С1опе() ( гесягп Кптя.иеапеги1яеС1опе О; ) ргачасе 1опд наг ; рганасе 1опо Ье1опк; ) мевЬегиазеС1опе — защищенный метод, реализованный в Буясеа.
ОЬ) есс, который может использоваться объектом для создания позерхиоскпной копии самого себя. Однако важно отметить одну особенность: МевЬегиаяес1опе создает копию объекта, ие вызывая никаких конструкторов для нового объекта. Это сокращение для создания объекта. Если объект полагается иа обязательный вызов конструктора во время создания — например, когда производится отладочная трассировка иа консоль во время создания объекта, — то МевЬеги).веС1опе ие подходит. Если же использование МеаЬегиазеС1опе обязательно,ио объект требует выполнения определенной работы 428 Глава 13 при вызове конструктора, то эту работу потребуется вынести в отдельный метод.
После вызова МещЬегы1веС1опе для создания нового экземпляра этот метод вызывается в конструкторе и в методе С1опе. Хотя такой подход возможен, все же он довольно утомителен. Альтернативный способ реализации клона предусматривает применение приватного копирующего конструктора, как показано в следующем коде: цв1по Яувгещ; рцЬ1гс яеа1еб с1аяв Огщепягопв: 1С1опеаЬ1е ( рцбьгс Пьщепягопя( 1опо м1бГЬ, 1опп Ье1дьг ) ( Сопяо1е.ыг1ге11пе( "Вызван Ььщепягопв( 1опс, 1опп )" ); СЬ1я.ы1сЬЬ = ы1бГЬ; СЬ1я.ье1оьг = Ье1дьг; // приватный копирующий конструктор, // используемый при создании копии этого объекта. рг1гасе Ььщепягопя( Рьщепвьопв оеьег ) ( Сопво1е.кггоепьпе( "Вызван Пгщепвгопя( О1щепвьопя )" ) Сьья.ыгбгь = ояьег.ы1бььв СЬ1я.ье1оьг = огьег.ье1оЬС; ) // Реализация 1С1опеаЬ1е.
рцЬ1гс оЬ1есс С1опе() ( гесцгп пеы Ььщепягопя(яьгв) ) рг1Уаге 1опс н1бгЬ) рг1уаве 1опо Ье1сЬС; ) Этот метод клонирования объектов является наиболее безопасным в том смысле, что позволяет сохранить полный контроль над тем, как выполняется копирование. Любые изменения. которые должны быть внесены в порядок копирования, могут быть сделаны в копирующем конструкторе. Следует учитывать то, что происходит в результате объявления конструктора класса — в этом случае компилятор не создает конструктор по умолчанию, что обычно делается, если никакого конструктора не предусмотрено.
Если приватный конструктор копирования окажется единственным определенным в классе, пользователи этого класса никогда не смогут создавать его экземпляры. Причина в том, что конструктор по умолчанию пропадает, а никакого другого общедоступного конструктора нес В данном случае можно не беспокоиться, поскольку определен общедоступный конструктор, принимающий два параметра. Тем не менее, это важный момент, который следует учитывать при проектировании класса. Теперь давайте также рассмотрим объекты, которые содержат в себе ссылки на другие объекты.
Предположим, имеется база данных сотрудников, а каждый сотрудник представлен объектом типа Ещр1оуее. Этот тип Вщр1оуее содержит важнейшую информацию, такую как имя сотрудника, его звание и идентификационный номер. Имя и, возможно, форматированный идентификационный номер представлены в виде строк, которые сами по себе являются объектами ссылочного типа.











