Сохранен 257
https://2ch.hk/b/res/92623291.html
У Архивача частично повредилась база данных из-за аппаратных проблем, ведётся восстановление. Временно могут не выводиться любые изображения и тэги тредов, а также тормозить вывод списка тредов.
Изменился адрес Архивача в сети Tor — arhivachqqqvwqcotafhk4ks2he56seuwcshpayrm5myeq45vlff44yd.onion. Установите Tor Browser для беспрепятственного доступа!
Аноним 10/05/15 Вск 01:56:18 #1 №92623291 
14312121782630.jpg
Пацаны, только что передернул на это:
http://exhentai.org/g/813136/5418e72702/
Это блядь самый пиздатый трапо-хентай, на который дрочил, нарисовано просто охуительно.
Я кончил радугой блядь.
Loading...
Аноним 10/05/15 Вск 02:02:10 #2 №92623594 
http://gelbooru.com/index.php?page=post&s=list&tags=nemunemu_%28candy_paddle%29

http://nhentai.net/g/120782/

http://nhentai.net/g/131372/
Аноним 10/05/15 Вск 02:02:14 #3 №92623603 
14312125348550.jpg
>>92623291
Сейчас набигут немогущие в панду аутисты.
Аноним 10/05/15 Вск 02:03:04 #4 №92623638 
>>92623291
Видел и лучше.
Аноним 10/05/15 Вск 02:05:25 #5 №92623761 
Вот еще норм, из этой же оперы.
http://exhentai.org/g/751362/4729e99e41/
http://exhentai.org/s/9796f4f05d/769518-1
http://exhentai.org/s/a0eb4ff99f/587382-17
Аноним 10/05/15 Вск 02:10:35 #6 №92624005 
>>92623291
ОП, советую зайти сюда http://boards.4chan.org/b/thread/614764458
Пока не смыло.
Аноним 10/05/15 Вск 02:14:20 #7 №92624191 
>>92623603
Аутист в трэде. Что делать чтобы была не панда, а сабж?
Аноним 10/05/15 Вск 02:16:15 #8 №92624289 
>>92624191
Бля, уже шишка набухла. Суки, как убрать панду?
Аноним 10/05/15 Вск 02:17:20 #9 №92624338 
14312134401870.jpg
Палю годноту
http://hentai2read.com/anal_angel/1/1/
Аноним 10/05/15 Вск 02:17:54 #10 №92624360 
Бедный ОП еще не знает про nagi ichi
http://exhentai.org/g/624395/0e596d6d54/
Аноним 10/05/15 Вск 02:17:57 #11 №92624364 
2015
дрочить на картинки
Аноним 10/05/15 Вск 02:18:26 #12 №92624382 DELETED
>>92624360
какого хуя везде панда открывается
Аноним 10/05/15 Вск 02:18:27 #13 №92624384 
>>92624289
Расширение скачай на браузер, в гугле есть все.

http://exhentai.org/s/aab514caea/516217-1
http://exhentai.org/s/f866fdb915/316871-1
Аноним 10/05/15 Вск 02:18:30 #14 №92624389 
http://exhentai.org/s/e2124431ea/784972-1
Просто оставлю это здесь.
Аноним 10/05/15 Вск 02:18:43 #15 №92624402 
>>92624364
Блядь, а куда деваться.
Ебанет в голову фетиш и кто теперь виноват? Дрочу на трапов, девушка есть.
Аноним 10/05/15 Вск 02:18:50 #16 №92624405 
>>92624360
Кстати знаю, уже дрочил на это.
Аноним 10/05/15 Вск 02:18:59 #17 №92624417 
>>92624364
>2015
>обосраться с разметкой
Аноним 10/05/15 Вск 02:19:47 #18 №92624455 
>>92624405
И как ты можешь говорить, что тот среднячок в ОП-посте лучше чем nagi ichi?
Аноним 10/05/15 Вск 02:19:54 #19 №92624462 
14312135949600.jpg
Еще трапоты

http://exhentai.org/g/564527/2134b1ee01/
Аноним 10/05/15 Вск 02:20:33 #20 №92624490 
14312136335570.jpg
>>92624191
Купить.
Аноним 10/05/15 Вск 02:21:04 #21 №92624514 
14312136649070.jpg
>>92624338
Вот еще того же мастера, не благодарите.
http://hentai2read.com/anal_justice/1/
http://hentai2read.com/anal_justice/2/
Аноним 10/05/15 Вск 02:21:08 #22 №92624516 
>>92624462
Хуйня
http://exhentai.org/g/454139/dcae34f185/
Аноним 10/05/15 Вск 02:22:06 #23 №92624565 DELETED
Идите нахуй со своей пандой, дауны.
Аноним 10/05/15 Вск 02:23:22 #24 №92624627 
>>92624382
Скачай расширение - гугли sad pand [название своего браузера]
Аноним 10/05/15 Вск 02:23:39 #25 №92624641 
>>92624455
Я кончил как из бронсбойтера, когда дрочил. А обычно вялого гоняю.
Критерий объективней некуда.

http://exhentai.org/g/747043/9773115574/
Аноним 10/05/15 Вск 02:23:56 #26 №92624657 
>>92624565
Ну это как посмотреть, кто еще даун...
sageАноним 10/05/15 Вск 02:24:02 #27 №92624661 
Толсто блядь, все знают что ваша панда - простая наебка, что никакого exhentai нет.
Аноним 10/05/15 Вск 02:24:23 #28 №92624678 
>>92624384
А на ведроид есть че?
Аноним 10/05/15 Вск 02:24:25 #29 №92624681 DELETED
>>92624627
sad pand opera - нихуя не нашлось
Аноним 10/05/15 Вск 02:26:54 #30 №92624789 
14312140147650.jpg
>>92624641
Норм, тогда еще Lokon и abgrund по этому же сайту ищи.
Я вообще походу на все это успел передрочить, пиздец. Ты вот эту хуйню скинул - а я на нее дрочил уже.
Вот это неплохая шняга.
http://exhentai.org/g/788179/6ea8233b20/
sageАноним 10/05/15 Вск 02:26:57 #31 №92624793 
>статичные картинки
>черно-белые
>2015
ниже этого только эротические рассказы
Аноним 10/05/15 Вск 02:26:57 #32 №92624794 
http://exhentai.org/g/780616/53473d889c/
sageАноним 10/05/15 Вск 02:27:06 #33 №92624801 
Вы еще вишмастер скачайте и фингербокс закажите. панда, блин.
Аноним 10/05/15 Вск 02:28:05 #34 №92624838 
>>92624681
Хуй знает, зачем тебе вообще опера, ставь фаерфокс или хром. Ну или ищи дольше 5 минут, как тебе эту хуйню обойти.
Аноним 10/05/15 Вск 02:28:52 #35 №92624882 
>>92624793
В статичных картинках красота, которая никогда не будет передана в динамике.
Разные средства выражения, детонька.

Это все равно, что сравнивать пар с водой и льдом.

sageАноним 10/05/15 Вск 02:28:54 #36 №92624883 
>>92624384
Ага, щас.
Аноним 10/05/15 Вск 02:29:27 #37 №92624898 
14312141670060.png
>>92624882
Аноним 10/05/15 Вск 02:29:47 #38 №92624912 
>>92624801
Ты блядь контуженый, да?
Как тебе, скажи на милость, пруфануть, что у меня все эти ссылки нормально открываются?
На хром и фаерфокс есть изи расширения, регаешься на e-hentai за 5 секунд, ставишь расширения, все заебись. Убей себя блядь.
Аноним 10/05/15 Вск 02:30:43 #39 №92624948 
>>92624912
Это троллинг тупостью же был с его стороны.
Хотя он от этого не меньший дурак.
Аноним 10/05/15 Вск 02:31:25 #40 №92624981 
14312142852110.jpg
Буду всякую хуйню кидать что ли
Или не кидать?
Аноним 10/05/15 Вск 02:31:36 #41 №92624986 
>>92624912
Так всетаки e-hentai?
Аноним 10/05/15 Вск 02:32:52 #42 №92625051 
14312143727070.png
14312143727371.jpg
3 литра кончи у меня где-то ушло на этого художника
Аноним 10/05/15 Вск 02:32:55 #43 №92625057 DELETED
>>92624912
>регистрации, расширения, ставить ебучие левые браузеры, трояны в браузере
>убить на все это дохуя времени
>все, чтобы посмотреть сраную картинку, которая на других сайтах за секунду без ебли грузится
Убей-ка ты себя сам, даун.
Аноним 10/05/15 Вск 02:33:33 #44 №92625091 
>>92624986
На e-hentai реагешься
Ставишь расширения чтобы убрать садпанду
А заходишь на exhentai.org
Аноним 10/05/15 Вск 02:34:15 #45 №92625119 
14312144551310.png
>>92624801
В гугле полно манов как панду убрать к хуям.
Подсказка: зарегаеся на ехентае
Аноним 10/05/15 Вск 02:34:27 #46 №92625127 
14312144671630.jpg
>>92625057
Лол, ты в этом треде времени провел больше, эффективный ты наш.
Сидит в трапо-треде на сосаче в 3 часа ночи и что-то пиздит про потерю времени.
Всегда охуевал с таких.
Аноним 10/05/15 Вск 02:35:06 #47 №92625162 
>>92625091
А если я не хочу всякий левак в браузер ставить?
Аноним 10/05/15 Вск 02:35:14 #48 №92625171 DELETED
>>92625119
Нахуй нам еще регаться с мылами и сотовыми на твоем сраном спам-сайте, где одна реклама небось.
Аноним 10/05/15 Вск 02:36:12 #49 №92625207 
14312145720250.png
14312145720491.jpg
>>92625057
>Убей-ка ты себя сам, даун.
Сказал слюнявый дебил, который ждет не дождется когда я еще сюда картинок с exhentai вброшу
Расширение я поставил 1 раз
Потом никакой ебли, ничего не надо запускать - вбиваешь в адрусную строку и дрочишь как умалишенный.
Вот тебе еще, бедный аутист
Аноним 10/05/15 Вск 02:36:50 #50 №92625235 DELETED
>>92625127
Слышь ты, не твоя личная армия, всякой хуйней для тебя заниматься и время с ебучими регистрациями убивать.
Аноним 10/05/15 Вск 02:36:53 #51 №92625238 
>>92625171
>ehentai
>сотовый
Аноним 10/05/15 Вск 02:37:40 #52 №92625277 DELETED
>>92625207
Оно не ставится на сафари на маке, дебил.
Аноним 10/05/15 Вск 02:37:51 #53 №92625284 
>>92625235
Ты ебанутый?
Где я просил вообще заниматься чем для меня.
Мне вообще похуй, как ты там.
sageАноним 10/05/15 Вск 02:38:05 #54 №92625298 
>>92625207
Любой нормальный человек не будет ебаться и ставить манеры а просто пойдет на форчан.
Аноним 10/05/15 Вск 02:38:34 #55 №92625325 DELETED
>>92625284
Ну и нехуй тут свой говносайт спамить, на который все равно никто не попадет.
Аноним 10/05/15 Вск 02:38:48 #56 №92625334 
14312147287210.jpg
14312147287361.jpg
>>92625162
Тогда охуевай как у тебя пухнет хер, пока мы все счастливо дрочим на exhentai
Блядь, я поставил 1 расширение.
Оно заменяет садпанду на окошко логин-пароль
ОХУЕТЬ КАКОЙ МУСОР, БРАУЗЕР В ГОВНЕ, ПИШУ С ЭСПЛОРЕРА
Аноним 10/05/15 Вск 02:38:53 #57 №92625338 
>>92625298
>использует форчан для хентая

И вк наверное для музыки.
Плебеи, что с них взять
Аноним 10/05/15 Вск 02:39:16 #58 №92625358 
>>92625277
Эх, как меня твои проблемы вообще ебут, уснуть не могу
sageАноним 10/05/15 Вск 02:39:45 #59 №92625391 
>>92625338
Уж лучше чем майнеры ставить.
Аноним 10/05/15 Вск 02:39:53 #60 №92625402 
>>92624627

что оно делает?
Аноним 10/05/15 Вск 02:40:03 #61 №92625416 
14312148037380.jpg
14312148037411.jpg
http://yaoihavenreborn.com/doujinshi/shota-doujinshi

http://yaoihavenreborn.com/gallery/shota
Аноним 10/05/15 Вск 02:40:12 #62 №92625423 DELETED
>>92625334
>мы все счастливо дрочим
У тебя раздвоение личности, долбоеб. Кроме тебя тут все панду видят, а не ебутся по 100500 часов с настройками какого-то говна для спамеров хуй знает зачем.
Аноним 10/05/15 Вск 02:40:26 #63 №92625443 
14312148263100.jpg
>>92625298
Так пиздуй, ебанат.
Аноним 10/05/15 Вск 02:40:47 #64 №92625468 
>>92625325
Эй даун с синдромом вахтера, ты мне не говори что и как делать, таких советчиков на хую крутил.
Ты зашел в мой тред и стал что-то кудахтать, соответственно тебе что-то от меня нужно, а не наоборот.
Нахуй иди да.
sageАноним 10/05/15 Вск 02:41:34 #65 №92625499 DELETED
>>92625468
Иди нахуй со своим ебучим тредом, где все одну панду видят. Сажи долбоебу.
sageАноним 10/05/15 Вск 02:41:42 #66 №92625507 
>>92625443
Ну не рвись так, плиз. Ты приперся сюда о своим говном, еще и посылаешь кого-то.
Аноним 10/05/15 Вск 02:42:19 #67 №92625548 
14312149398240.jpg
>>92625423
Ты все как-то пытаешься задеть меня, вызвать бугурт, а меж тем, бугурт только у тебя одного
Лови картинку, мне тебя жаль
Аноним 10/05/15 Вск 02:42:45 #68 №92625567 
>>92625507
Я не приперся, я вкатился.
Аноним 10/05/15 Вск 02:43:03 #69 №92625578 
>>92625499
Я не виноват, что ебанашки-пидаранки не могут в панду.

>все
Нас тут как минимум двое, кто может.
Аноним 10/05/15 Вск 02:43:23 #70 №92625601 
>>92625578
>пидаранки
пидарашки
Аноним 10/05/15 Вск 02:43:38 #71 №92625611 
>>92623291
> http://exhentai.org/
Даже толстовато немного.
Аноним 10/05/15 Вск 02:44:11 #72 №92625639 
Долбоебы, тут можно и без всяких костылей на браузеры обойтись.
Аноним 10/05/15 Вск 02:44:17 #73 №92625645 
>>92625416
Блядь, я карочи приехал
дошло до того, что я даже имена этих художников знаю poju и hinemosunotari
додрочился блядь
sageАноним 10/05/15 Вск 02:45:28 #74 №92625699 
>>92625639
Вангую японский прокси или что-то типо того. Одним словом - нахуй.
sageАноним 10/05/15 Вск 02:45:56 #75 №92625724 DELETED
Сажи пидору
Аноним 10/05/15 Вск 02:46:56 #76 №92625774 
14312152164350.png
14312152164411.jpg
>>92625611
Чего ж тут толстого?
Аноним 10/05/15 Вск 02:48:18 #77 №92625849 
Дальше кидать?
Или все дебилы уже с форчана себе понакачали трапопорева?
sageАноним 10/05/15 Вск 02:48:43 #78 №92625870 DELETED
>>92625849
в жопу пошел
sageАноним 10/05/15 Вск 02:49:13 #79 №92625898 
Я, честно говоря, думал что в этом треде мы будем делится годными ссылками. А оказалось что ОП илитствующая вниманеблядь. САЖИ
sageАноним 10/05/15 Вск 02:49:16 #80 №92625901 DELETED

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
Аноним 10/05/15 Вск 02:49:35 #81 №92625932 DELETED
>>92625901

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
Аноним 10/05/15 Вск 02:49:59 #82 №92625957 
>>92625870
Как скажешь, разорванный.
sageАноним 10/05/15 Вск 02:50:00 #83 №92625959 DELETED
>>92625932

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
sageАноним 10/05/15 Вск 02:50:17 #84 №92625976 
>>92625849
Блядь, такое чувство что ты нам делаешь неоценимую услугу. Уебывай.
sageАноним 10/05/15 Вск 02:50:25 #85 №92625983 DELETED
>>92625959

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
sageАноним 10/05/15 Вск 02:50:41 #86 №92625999 DELETED

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
sageАноним 10/05/15 Вск 02:51:39 #87 №92626042 DELETED

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
sageАноним 10/05/15 Вск 02:51:59 #88 №92626059 DELETED
Как вы уже знаете, ваши программы могут обращаться к частным (private) элементам класса только с помощью функций-элементов этого же класса. Используя частные элементы класса вместо общих во всех ситуациях, где это только возможно, вы уменьшаете возможность программы испортить значения элементов класса, так как программа может обращаться к таким элементам только через интерфейсные функции (которые управляют доступом к частным элементам). Однако в зависимости от использования объектов вашей программы, иногда вы можете существенно увеличить производительность позволяя одному классу напрямую обращаться к частным элементам другого. В этом случае уменьшаются издержки (требуемое время выполнения) на вызов интерфейсных функций. В подобных ситуациях C++ позволяет определить класс в качестве друга (friend} другого класса и разрешает классу-другу доступ к частным элементам этого другого класса. В этом уроке объясняется, как ваши программы могут указать, что два класса являются друзьями. К концу данного урока вы освоите следующие основные концепции:

Используя ключевое слово friend, класс может сообщить C++, кто является его другом, т. е. другими словами, что другие классы могут обращаться напрямую к его частным элементам.
Частные элементы класса защищают данные класса, следовательно, вы должны ограничить круг классов-друзей только теми классами, которым действительно необходим прямой доступ к частным элементам искомого класса.
C++ позволяет ограничить дружественный доступ определенным набором функций.
Частные (private) элементы позволяют вам защищать классы и уменьшить вероятность ошибок. Таким образом, вы должны ограничить использование классов-друзей настолько, насколько это возможно. Иногда программа напрямую может изменить значения элементов класса, это увеличивает вероятность появления ошибок.

ОПРЕДЕЛЕНИЕ ДРУЗЕЙ КЛАССА

C++ позволяет друзьям определенного класса обращаться к частным элементам этого класса. Чтобы указать C++, что один класс является другом (friend) другого класса, вы просто помещаете ключевое слово friend и имя соответствующего класса-друга внутрь определения этого другого класса. Например, приведенный ниже класс book объявляет класс librarian своим другом. Поэтому объекты класса librarian могут напрямую обращаться к частным элементам класса book, используя оператор точку:

class book

{
public:
book (char , char , char *);
void show_book(void);
friend librarian;
private:
char title [64] ;
char author[64];
char catalog[64];
};

Как видите, чтобы указать друга, необходим только один оператор внутри определения класса. Например, следующая программа VIEWBOOK.CPP использует librarian в качестве друга класса book. Следовательно, функции класса librarian могут напрямую обращаться к частным элементам класса book. В данном случае программа использует функцию change_catalog класса librarian для изменения номера карточки каталога определенной книги:
sageАноним 10/05/15 Вск 02:52:16 #89 №92626076 DELETED
char catalog[64];
};

book::book(char title, char author, char •catalog)

{
strcpy(book::title, title);
strcpy(book::author, author) ;
strcpy(book::catalog, catalog);
}

void book::show_book(void)

{
cout << "Название: " << title << endl;
cout << "Автор: " << author << endl;
cout << "Каталог: " << catalog << endl;
}

class librarian

{
public:
void change_catalog(book , char );
char get_catalog(book);
};

void librarian::change_catalog(book
this_book, char new_catalog)

{
strcpy(this_book->catalog, new_catalog);
}

char
librarian: :get__catalog(book this_book)

{
static char catalog[64];
strcpy(catalog, this_book.catalog);
return(catalog) ;
}

void main(void)

{
book programming( "Учимся программировать на языке C++", "Jamsa", "P101");
librarian library;
programming.show_book();
library.change_catalog(&programming, "Легкий C++ 101");
programming.show_book();
}

Как видите, программа передает объект book в функцию change_catalog класса librarian по адресу. Поскольку эта функция изменяет элемент класса book, программа должна передать параметр по адресу, а затем использовать указатель для обращения к элементу этого класса. Экспериментируйте с данной программой, попробуйте удалить оператор friend из определения класса book. Поскольку класс librarian больше не имеет доступа к частным элементам класса book, компилятор C++ сообщает о синтаксических ошибках при каждой ссылке на частные данные класса book.

О друзьях класса

Обычно единственный способ, с помощью которого ваши программы могут обращаться к частным элементам класса, заключается в использовании интерфейсных функций. В зависимости от использования объектов программы иногда может быть удобным (или более эффективным с точки зрения скорости вычислений) разрешить одному классу обращаться к частным элементам другого. Для этого вы должны информировать компилятор C++, что класс является другом (friend). Компилятор, в свою очередь, позволит классу-другу обращаться к частным элементам требуемого класса. Чтобы объявить класс другом, поместите ключевое слово friend и имя класса-друга в секцию public определения класса, как показано ниже:

class abbott

{
public:
friend costello;
// Общие элементы
private:
// Частные элементы
};

Как друзья отличаются от защищенных (protected) элементов

Из урока 26 вы узнали, что в C++ существуют защищенные (protected) элементы класса, что позволяет производным классам обращаться к защищенным элементам базового
sageАноним 10/05/15 Вск 02:52:44 #90 №92626099 DELETED
другу обращаться к частным элементам требуемого класса. Чтобы объявить класс другом, поместите ключевое слово friend и имя класса-друга в секцию public определения класса, как показано ниже:

class abbott

{
public:
friend costello;
// Общие элементы
private:
// Частные элементы
};

Как друзья отличаются от защищенных (protected) элементов

Из урока 26 вы узнали, что в C++ существуют защищенные (protected) элементы класса, что позволяет производным классам обращаться к защищенным элементам базового класса напрямую, используя оператор точку. Помните, что к защищенным элементам класса могут обращаться только те классы, которые являются производными от данного базового класса, другими словами, классы, которые наследуют элементы базового класса (защищенные элементы класса являются как бы частными по отношению к остальным частям программы). Классы-друзья C++ обычно не связаны между собой узами наследования. Единственный способ для таких не связанных между собой классов получить доступ к частным элементам другого класса состоит в том, чтобы этот другой класс информировал компилятор, что данный класс является другом.

ОГРАНИЧЕНИЕ КОЛИЧЕСТВА ДРУЗЕЙ

Как вы только что узнали, если вы объявляете один класс другом другого класса, вы обеспечиваете классу-другу доступ к частным элементам данных этого другого класса. Вы также знаете и то, что чем больше доступа к частным данным класса, тем больше шансов на внесение ошибок в программу. Следовательно, если доступ к частным данным другого класса необходим только нескольким функциям класса, C++ позволяет указать, что только определенные функции дружественного класса будут иметь доступ к частным элементам. Предположим, например, что класс librarian, представленный в предыдущей программе, содержит много разных функций. Однако предположим, что только функциям change_catalog и get_catalog необходим доступ к частным элементам класса book. Внутри определения класса book мы можем ограничить доступ к частным элементам только этими двумя функциями, как показано ниже:

class book

{
public:
book(char , char , char );
void show_book(void);
friend char
librarian::get_catalog(book);
friend void librarian: :change_catalog( book , char );
private:
char title[64];
char author[ 64 ];
char catalog[64];
};

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

О функциях-друзьях

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

public:
friend class_name::function_name(parameter types);

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

Если ваша программа начинает ссылаться на один класс из другого, вы можете получить синтаксические ошибки, если порядок определения классов неверен. В данном случае определение класса book использует прототипы функций, определенные в классе librarian. Следовательно, определение класса librarian должно предшествовать определению класса book. Однако если вы проанализируете класс librarian, то обнаружите, что он ссылается на класс book:
sageАноним 10/05/15 Вск 02:53:07 #91 №92626119 DELETED
out << "Каталог: " << catalog << endl;
}

void librarian::change_catalog(book this_book, char new_catalog)

{
strcpy(this_book->catalog, new_catalog) ;
}

char *librarian::get_catalog(book this_book)

{
static char catalog[64];
strcpy(catalog, this_book.catalog);
return(catalog) ;
}

void main(void)

{
book programming( "Учимся программировать на C++", "Jamsa", "P101");
librarian library;
programming.show_book();
library.change_catalog(&programming, "Легкий C++ 101");
programming.show_book();
}

Как видите, программа сначала использует объявление, чтобы сообщить компилятору, что класс book будет определен позже. Поскольку объявление извещает компилятор о классе book, определение класса librarian может ссылаться на класс book, который еще не определен в программе.

Что такое идентификатор класса

Идентификатор представляет собой имя, например имя переменной или класса. Если ваши программы используют дружественные классы, то может случиться, что определение одного класса ссылается на другой класс (его имя или идентификатор), о котором компилятор C++ еще ничего не знает. В таких случаях компилятор C++ будет сообщать о синтаксических ошибках. Чтобы избавиться от ошибок типа "что следует определять сначала", C++ позволяет вам включать в начало исходного текста программы объявление класса, тем самым вводя идентификатор класса:

class class_name;

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

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

В данном уроке вы изучили, как использовать классы-друзья для обращения к частным элементам другого класса напрямую с использованием оператора точки. В уроке 29 вы изучите, как использовать в C++ шаблоны функций для упрощения определения подобных функций. Но прежде чем перейти к уроку 29 убедитесь, что вы освоили следующее:

Использование в ваших программах на C++ друзей позволяет одному классу обращаться к частным элементам другого класса напрямую, используя оператор точку.
Для объявления одного класса другом (friend) другого класса вы должны внутри определения этого другого класса указать ключевое слово friend, за которым следует имя первого класса.
После объявления класса другом по отношению к другому классу, все функции-элементы класса-друга могут обращаться к частным элементам этого другого класса.
Чтобы ограничить количество дружественных методов, которые могут обращаться к частным данным класса, C++ позволяет указать дружественные функции. Для объявления функции-друга вы должны указать ключевое слово friend, за которым следует прототип функции, которой, собственно, и необходимо обращаться к частным элементам класса.
При объявлении дружественных функций вы можете получить синтаксические ошибки, если неверен порядок определений классов. Если необходимо сообщить компилятору, что идентификатор представляет имя класса, который программа определит позже, вы можете использовать оператор такого вида class class_name;.
sageАноним 10/05/15 Вск 02:53:47 #92 №92626159 DELETED
На всем протяжении этой книги вы использовали выходной поток cout для вывода информации на экран дисплея. Аналогично, многие из ваших программ использовали входной поток cin для чтения информации с клавиатуры. Оказывается, cin и cout представляют собой классовые объекты, определяемые и создаваемые с помощью заголовочного файла iostream.h. Как объекты cin и cout поддерживают различные операторы и операции. Из данного урока вы узнаете, как расширить возможности ввода и вывода, используя функции, встроенные в классы cin и cout. К концу этого урока вы освоите следующие основные концепции:

Заголовочный файл iostream.h содержит определения классов, которые вы можете проанализировать, чтобы лучше понять потоковый ввод/вывод.
Используя метод cout.width, ваши программы могут управлять шириной вывода.
Используя метод cout.fill, ваши программы могут заменить пустые выходные символы (табуляцию и пробелы) некоторым определенным символом.
Для управления количеством цифр, выводимых выходным потоком cout для значений с плавающей точкой, ваши программы могут использовать метод cout.setprecision.
Для вывода и ввода по одному символу за один раз ваши программы могут использовать потоковые методы cout.put и cin.get.
Используя метод cin.getline, ваши программы могут вводить целую строку за один раз.
Почти любая создаваемая вами на C++ программа будет использовать cout или cin для выполнения операций В/В (ввода/вывода). Выберите время для экспериментов с программами из этого урока.

ЧТО ВНУТРИ iostream.h

Начиная с урока 1, каждая написанная вами на C++ программа включала заголовочный файл iostream.h. Этот файл содержит определения, позволяющие вашим программам использовать cout для выполнения вывода и cin для выполнения ввода. Более точно, этот файл определяет классы istream и ostream (входной поток и выходной поток), a cin и соut являются переменными (объектами) этих классов. Выберите время, чтобы напечатать файл iostream.h. Он находится в подкаталоге INCLUDE. Определения в этом файле достаточно сложны. Однако если вы пройдете по файлу медленно, то обнаружите, что большинство определений являются просто определениями классов и констант. Внутри файла вы найдете объявления переменных cin и cout.

ИСПОЛЬЗОВАНИЕ cout
Аноним 10/05/15 Вск 02:53:52 #93 №92626161 
>>92624627
>Error: Login Failure!
sageАноним 10/05/15 Вск 02:54:13 #94 №92626178 DELETED
Как вы уже знаете, cout представляет собой класс, который содержит несколько разных методов. Следующие программы иллюстрируют использование некоторых методов, которые ваши программы могут применять для форматирования вывода. Из урока 3 вы узнали, что манипулятор setw позволяет вашим программам указать минимальное количество символов, которое может занять следующее выходное значение:

#include <iostream.h>

#include <iomanip.h>

void main(void)

{
cout << "Мое любимое число" << setw(3) << 1001 << endl;
cout << "Мое любимое число" << setw (4) << 1001 << endl;
cout << "Мое любимое число" << setw (5) << 1001 << endl;
cout << "Мое любимое число" << setw(6) << 1001 << endl;
}

Подобным образом метод cout.width позволяет вам указать минимальное количество символов, которое будет использовать сои/для вывода следующего значения. Следующая программа COUTWIDT.CPP использует функцию cout.width для выполнения работы, аналогичной той, которую выполняет setw, что и показано ниже:

#include <iostream.h>

#include <iomanip.h>

void main (void)

{
int i;
for (i = 3; i < 7; i++)

{
cout << "Мое любимое число";
cout. width (i);
cout << 1001 << endl;
}
}

Если вы откомпилируете и запустите вашу программу, на экране дисплея появится следующий вывод:

С:\> COUTWIDT <ENTER>

Мое любимое число1001

Мое любимое число 1001

Мое любимое число 1001

Мое любимое число 1001

Подобно манипулятору setw, ширина, выбираемая с помощью функции cout.width, действует только для следующего выходного значения.

Использование символа-заполнителя

Если вы используете манипулятор setw или функцию cout.width для управления шириной вывода, cout будет помещать пробелы до (или после для выровненных влево) значений, как это и требуется. В зависимости от назначения вашей программы вы, возможно, захотите использовать символ, отличный от пробела. Предположим, например, что ваша программа создает такую таблицу:

Таблица информации
Профиль компании................................................ 10
Доходы и убытки компании...................................11
Члены правления компании..................................13

В данном случае вывод предваряет номера страниц точками. Функция cout.fill позволяет вам указать символ, который cout будет использовать для заполнения пустого пространства. Следующая программа COUTFILL.CPP создает таблицу, подобную приведенной выше:

#include <iostream.h>

#include <iomanip.h>

void main(void)

{
cout << "Таблица информации" << endl;
cout.fill (' . ');
cout << "Профиль компан
sageАноним 10/05/15 Вск 02:54:36 #95 №92626207 DELETED
Если вы однажды выбрали символ-заполнитель с помощью cout.fill, он будет оставаться действительным, пока вы не измените его повторным вызовом cout.fill.

Управление цифрами значений с плавающей точкой

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

#include <iostream.h>

#include <iomanip.h>

void main(void)

{
float value = 1.23456;
int i;
for (i = 1; i < 6; i++) cout << setprecision(i) << value << endl;
}

Когда вы откомпилируете и запустите эту программу, на экране дисплея появится следующий вывод:

С:\>SETPREC <ENTER>

1.2

1.23

1.235

1.2346

1.23456

Если вы используете манипулятор setprecision для изменения точности, ваша установка действует до тех пор, пока программа повторно не использует setprecision.

ВЫВОД И ВВОД ОДНОГО СИМВОЛА ЗА ОДИН РАЗ

В зависимости от назначения вашей программы вам, возможно, потребуется выводить символы на дисплей или читать с клавиатуры по одному символу за один раз. Для вывода одного символа за один раз ваши программы могут использовать функцию cout.put. Следующая программа COUTPUT.CPP использует эту функцию для вывода на экран сообщения Учимся программировать на языке C++! по одному символу за раз:

#include <iostream.h>

void main(void)

{
char string[] = "Учимся программировать на языке C++!";
int i;
for (i = 0; string; i++) cout.put(string) ;
}

Библиотека этапа выполнения предоставляет функцию с именем toupper, которая возвращает заглавный эквивалент строчной буквы. Следующая программа COUTUPPR.CPP использует функцию toupper для преобразования символа в верхний регистр, а затем выводит эту букву с помощью cout.put.

#include <iostream.h>

#include <ctype.h> // прототип toupper

void main(void)

{
sageАноним 10/05/15 Вск 02:55:01 #96 №92626223 DELETED

char string[] = "Учимся программировать на языке C++!";
int i;
for (i = 0; string; i++) cout.put(string) ;
}

Библиотека этапа выполнения предоставляет функцию с именем toupper, которая возвращает заглавный эквивалент строчной буквы. Следующая программа COUTUPPR.CPP использует функцию toupper для преобразования символа в верхний регистр, а затем выводит эту букву с помощью cout.put.

#include <iostream.h>

#include <ctype.h> // прототип toupper

void main(void)

{
char string[] = "C++ language";
int i;
for (i = 0; string; i++) cout.put(toupper(string));
cout << endl << "Результирующая строка: " << string << endl;
}

Если вы откомпилируете и запустите эту программу, на экране дисплея появится следующий вывод:

С:\> COUTUPPR <ENTER>

C++ LANGUAGE

Результирующая строка: C++ language

К сожалению, функция toupper применима только к английским буквам. Прим. перев.

ЧТЕНИЕ ВВОДА С КЛАВИАТУРЫ ПО ОДНОМУ СИМВОЛУ ЗА РАЗ

Точно так же, как cout предоставляет функцию cout.put для вывода символа, cin предоставляет функцию cin.get, которая позволяет вам читать один символ данных. Чтобы воспользоваться функцией cin.get, вы просто присваиваете переменной возвращаемый этой функцией символ, как показано ниже:

letter = cin.get();

Следующая программа CIN_GET.CPP выводит сообщение, в ответ на которое вам необходимо ввести Y или N. Затем она повторяет в цикле вызов cin.get для чтения символов, пока не получит Y или N:

#include <iostream.h>

#include <ctype.h>

void main(void)

{
char letter;
cout << "Хотите продолжать? (Y/N): ";
do

{
letter = cin.get();
// Преобразовать к верхнему регистру
letter = toupper(letter);
} while ((letter != 'Y') && (letter != 'N'));
cout << endl << "Вы ввели " << letter << endl;
}

ЧТЕНИЕ С КЛАВИАТУРЫ ЦЕЛОЙ СТРОКИ

Как вы уже знаете, при использовании cin для выполнения ввода с клави
sageАноним 10/05/15 Вск 02:55:04 #97 №92626225 
>>92626178
Это C#?
sageАноним 10/05/15 Вск 02:55:23 #98 №92626236 DELETED
ак пробел, табуляция или возврат каретки, для определения, где заканчивается одно значение и начинается другое. Во многих случаях вы захотите, чтобы ваши программы считывали целую строку данных в символьную строку. Для этого программы могут использовать функцию cin.getline. Для использования cin.getline вам необходимо указать символьную строку, в которую будут помещаться символы, а также размер строки, как показано ниже:

cin.getline(string, 64);

Когда cin.get читает символы с клавиатуры, она не будет читать символов больше, чем может вместить строка. Удобным способом определить размер массива является использование оператора C++ sizeof, как показано ниже:

сin.getline(string, sizeof(string));

Если позже вы измените размер массива, то вам не нужно будет искать и изменять каждый оператор с cin.get, встречающийся в вашей программе. Вместо этого оператор sizeof ' будет использовать корректный размер массива. Следующая программа GETLINE.CPP использует функцию cin.getline для чтения с клавиатуры строки текста:

#include <iostream.h>

void main(void)

{
char string[128];
cout << "Введите строку текста и нажмите Enter" << endl;
cin.getline(string, sizeof(string));
cout << "Вы ввели: " << string << endl;
}

Когда вы читаете символы с клавиатуры, то, возможно, вам понадобится читать символы вплоть до и включая определенный символ. Когда такой символ будет прочитан, возможно, вы захотите завершить операцию ввода. Для выполнения подобной операции ваша программа может передать искомый символ в cin.getline. Например, следующий вызов заставляет функцию cin.getline читать строку текста, пока не встретится возврат каретки, или пока не будут прочитаны 64 символа, или пока не встретится буква Я:

cin.getline(string, 64, 'Я');

Следующая программа UNTIL_Z.CPP использует cin.getline для чтения строки текста или символов вплоть до появления буквы Я (включая и эту букву):

#include <iostream.h>

void main(void)

{
char string[128];
cout << "Введите строку текста и нажмите Enter" << endl;
cin.getline(string, sizeof(string), 'Я');
cout << "Вы ввели: " << string << endl;
}

Откомпилируйте и запустите эту программу. Экспериментируйте с различными строками текста. Некоторые из них начинайте с буквы Я, некоторые заканчивайте буквой Я, а некоторые пусть вообще не содержат букву Я.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Каждая созданная вами на C++ программа будет, вероятно, использовать cin или cout для выполнения операций ввода и вывода. Этот урок посвящен некоторым манипуляторам В/В и функциям, которые вы можете использовать с потоками cin и cout. По мере усложнения ваших программ они часто будут сохранять информацию в файлах. Из урока 34 вы узнаете, как в C++ выполнять операции файлового ввода и вывода. Прежде чем приступить к изучению урока 34, убедитесь, что вы освоили следующие основные концепции:

cin и cout являются объектами (переменными) классов i stream и ostream, которые определены в заголовочном файле iostream.h. А если так, они предоставляют функции, которые ваши программы могут вызывать для решения определенных задач.
Функция cout.width позволяет вашим программам указать минимальное количество символов, которые будет использовать следующее выходное значение.
Функция cout. fill позволяет вашим программам указать символ, который cout будет использовать для заполнения пустого пространства устанавливаемого с помощью cout.width или setw.
Манипулятор setprecision позволяет вашим программам управлять количеством цифр, выводимых справа от десятичной точки для значений с плавающей точкой.
Функции cin.get и cout.put позволяют вашим программам вводить или выводить один символ.
Функция cin.getline позволяет вашим программам читать строку текста с клавиатуры.
sageАноним 10/05/15 Вск 02:55:53 #99 №92626263 DELETED
Как вы уже знаете, единственное неудобство при использовании функций состоит в том, что они увеличивают издержки (увеличивают время выполнения), помещая параметры в стек при каждом вызове. Из данного урока вы узнаете, что для коротких функций можно использовать метод, называемый встроенным кодом, который помещает операторы функции для каждого ее вызова прямо в программу, избегая таким образом издержек на вызов функции. Используя встроенные (inline) функции, ваши программы будут выполняться немного быстрее. К концу этого урока вы освоите следующие основные концепции:

Для улучшения производительности за счет уменьшения издержек на вызов функции вы можете заставить компилятор C++ встроить в программу код функции, подобно тому, как это делается при замещении макрокоманд.
Используя встроенные (inline) функции, ваши программы остаются удобными для чтения (читающий программу видит вызов функции), но вы избегаете издержек на вызов функции, которые вызваны помещением параметров в стек и их последующим извлечением из стека, а также переходом к телу функции и последующим возвратом из нее.
В зависимости от требований, предъявляемых к вашей программе, иногда вам потребуется использовать язык ассемблера для решения определенной задачи.
Для упрощения применения программирования на языке ассемблера C++ позволяет определить функции на встроенном языке ассемблера внутри ваших программ на C++.
ВСТРОЕННЫЕ ФУНКЦИИ

Когда вы определяете в своей программе функцию, компилятор C++ переводит код функции в машинный язык, сохраняя только одну копию инструкций функции внутри вашей программы. Каждый раз, когда ваша программа вызывает функцию, компилятор C++ помещает в программу специальные инструкции, которые заносят параметры функции в стек и затем выполняют переход к инструкциям этой функции. Когда операторы функции завершаются, выполнение программы продолжается с первого оператора, который следует за вызовом функции. Помещение аргументов в стек и переход в функцию и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее, чем если бы она размещала те же операторы прямо внутри программы при каждой ссылке на функцию. Например, предположим, что следующая программа CALLBEEP.CPP вызывает функцию show_message, которая указанное число раз выдает сигнал на динамик компьютера и затем выводит сообщение на дисплей:

#include <iostream.b>

void show_message(int count, char *message)

{
int i;
for (i = 0; i < count; i++) cout << '\a';
cout << message << endl;
}

void main(void)

{
show_message(3, "Учимся программировать на языке C++");
show_mes sage(2, "Урок 35");
}

Следующая программа NO_CALL.CPP не вызывает функцию show_message. Вместо этого она помещает внутри себя те же операторы функции при каждой ссылке на функцию:

#include <iostream.h>

void main (void)

{
int i;
for (i = 0; i < 3; i++) cout << '\a';
cout << " Учимся программировать на языке C++" << endl;
for (i = 0; i < 2; i++) cout << '\a';
cout << "Урок 35" << endl;
}
sageАноним 10/05/15 Вск 02:56:12 #100 №92626275 DELETED
Обе программы выполняют одно и то же. Поскольку программа NO_CALL не вызывает функцию show_message, она выполняется немного быстрее, чем программа CALLBEEP. В данном случае разницу во времени выполнения определить невозможно, но, если в обычной ситуации функция будет вызываться 1000 раз, вы, вероятно, заметите небольшое увеличение производительности. Однако программа NO_CALL более запутана, чем ее двойник CALL_BEEP, следовательно, более тяжела для восприятия.

При создании программ вы всегда должны попытаться определить, когда лучше использовать обычные функции, а когда лучше воспользоваться встроенными функциями. Для более простых программ предпочтительно использовать обычные функции. Однако, если вы создаете программу, для которой производительность имеет первостепенное значение, вам следовало бы уменьшить количество вызовов функций. Один из способов уменьшения количества вызовов функций состоит в том, чтобы поместить соответствующие операторы прямо в программу, как только что было сделано в программе NO_CALL. Однако, как вы могли убедиться, замена только одной функции внесла значительную путаницу в программу. К счастью, C++ предоставляет ключевое слово inline, которое обеспечивает лучший способ.

Использование ключевого слова inline

При объявлении функции внутри программы C++ позволяет вам предварить имя функции ключевым словом inline. Если компилятор C++ встречает ключевое слово inline, он помещает в выполнимый файл (машинный язык) операторы этой функции в месте каждого ее вызова. Таким образом, можно улучшить читаемость ваших программ на C++, используя функции, и в то же время увеличить производительность, избегая издержек на вызов функций. Следующая программа INLINE.CPP определяет функции тах и min как inline:

#include <iostream.h>

inline int max(int a, int b)

{
if (a > b) return(a);
else return(b) ;
}

inline int min(int a, int b)

{
if (a < b) return(a);
else return(b);
}

void main(void)

{
cout << "Минимум из 1001 и 2002 равен " << min(1001, 2002) << endl;
cout << "Максимум из 1001 и 2002 равен " << max(1001, 2002) << endl;
}

В данном случае компилятор C++ заменит каждый вызов функции на соответствующие операторы функции. Производительность программы увеличивается без ее усложнения.
sageАноним 10/05/15 Вск 02:56:34 #101 №92626298 DELETED
О встроенных функциях

Если компилятор C++ перед определением функции встречает ключевое слово inline, он будет заменять обращения к этой функции (вызовы) на последовательность операторов, эквивалентную выполнению функции. Таким образом ваши программы улучшают производительность, избавляясь от издержек на вызов функции и в то же время выигрывая в стиле программы, благодаря использованию функций.

ВСТРОЕННЫЕ ФУНКЦИИ И КЛАССЫ

Как вы уже знаете, при определении класса вы определяете функции этого класса внутри или вне класса. Например, класс employee определяет свои функции внутри самого класса:

class employee

{
public:
employee(char name, char position, float salary)

{
strcpy(employee::name, name);
strcpy(employee::position, position);
employee::salary = salary;
}

void show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Должность: " << position << endl;
cout << "Оклад: $" << salary << endl;
}

private:
char name [64];
char position[64];
float salary;
};

Размещая подобным образом функции внутри класса, вы тем самым объявляете их встроенными {inline). Если вы создаете встроенные функции класса этим способом, C++ дублирует функцию для каждого создаваемого объекта этого класса, помещая встроенный код при каждой ссылке на метод (функцию) класса. Преимущество такого встроенного кода состоит в увеличении производительности. Недостатком является очень быстрое увеличение объема самого определения класса. Кроме того, включение кода функции в определение класса может существенно запугать класс, делая его элементы трудными для восприятия.

Для улучшения читаемости определений ваших классов вы можете вынести функции из определения класса, как вы обычно и делаете, и разместить ключевое слово inline перед определением функции. Например, следующее определение заставляет компилятор использовать встроенные операторы для функции show_employee:

inline void employee::show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Должность: " << position << endl;
cout << "Оклад: $" << salary << endl;
}

ИСПОЛЬЗОВАНИЕ ОПЕРАТОРОВ ЯЗЫКА АССЕМБЛЕРА
sageАноним 10/05/15 Вск 02:56:56 #102 №92626324 DELETED
ак вы знаете из урока 1, программисты могут создавать программы, используя широкий спектр языков программирования. Затем компилятор преобразует операторы программы в машинный код (нули и единицы), который понимает компьютер. Каждый тип компьютеров поддерживает промежуточный язык, называемый языком ассемблера, который попадает в категорию между машинным языком и языком программирования, таким как C++.

Язык ассемблера использует другие символы для представления инструкций машинного языка. В зависимости от назначения ваших программ, возможно, вам потребуется выполнить операции низкого уровня, для которых необходимо использовать операторы языка ассемблера. В таких случаях вы можете использовать оператор C++ asm для встраивания операторов языка ассемблера в программу. Большинство создаваемых вами программ не потребуют операторов языка ассемблера. Следующая программа USE_ASM.CPP использует оператор asm, чтобы вставить операторы языка ассемблера, необходимые для озвучивания динамика компьютера в среде MS-DOS:

#include <iostream.h>

void main(void)

{
cout << "Сейчас будет звонить!" << endl;
asm

{
MOV AH,2
MOV DL,7
INT 21H
}

cout << "Есть!" << endl;
}

Как видите, используя оператор asm, программа комбинирует C++ и операторы языка ассемблера.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Встроенные функции улучшают производительность ваших программ, уменьшая издержки на вызов функций. Из этого урока вы узнали, как и где использовать встроенные функции в своих программах. Вы также узнали, что иногда вашим программам необходимо использовать язык ассемблера для решения определенных задач. Из урока 36 вы узнаете, как ваши программы могут обратиться к аргументам командной строки, которые пользователь вводит при запуске программы. Однако, прежде чем перейти к уроку 36, убедитесь, что вы освоили следующие основные концепции:

Помещение параметров в стек и переход к функции и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее.
Ключевое слово inline заставляет компилятор C++ заменять вызов функции эквивалентной последовательностью операторов, которые бы выполняла эта функция. Поскольку встроенные операторы избавляют от издержек на вызов функции, программа будет выполняться быстрее.
Если вы используете встроенные функции внутри класса, каждый создаваемый вами объект использует свои собственные встроенные операторы. Обычно все объекты одного и того же класса совместно используют один и тот же код функции.
Ключевое слово asm позволяет вам встраивать операторы языка ассемблера в программы на C++
sageАноним 10/05/15 Вск 02:57:19 #103 №92626347 DELETED
Начиная с урока 8, ваши программы интенсивно использовали функции. Как вы уже знаете, единственное неудобство при использовании функций состоит в том, что они увеличивают издержки (увеличивают время выполнения), помещая параметры в стек при каждом вызове. Из данного урока вы узнаете, что для коротких функций можно использовать метод, называемый встроенным кодом, который помещает операторы функции для каждого ее вызова прямо в программу, избегая таким образом издержек на вызов функции. Используя встроенные (inline) функции, ваши программы будут выполняться немного быстрее. К концу этого урока вы освоите следующие основные концепции:

Для улучшения производительности за счет уменьшения издержек на вызов функции вы можете заставить компилятор C++ встроить в программу код функции, подобно тому, как это делается при замещении макрокоманд.
Используя встроенные (inline) функции, ваши программы остаются удобными для чтения (читающий программу видит вызов функции), но вы избегаете издержек на вызов функции, которые вызваны помещением параметров в стек и их последующим извлечением из стека, а также переходом к телу функции и последующим возвратом из нее.
В зависимости от требований, предъявляемых к вашей программе, иногда вам потребуется использовать язык ассемблера для решения определенной задачи.
Для упрощения применения программирования на языке ассемблера C++ позволяет определить функции на встроенном языке ассемблера внутри ваших программ на C++.
ВСТРОЕННЫЕ ФУНКЦИИ

Когда вы определяете в своей программе функцию, компилятор C++ переводит код функции в машинный язык, сохраняя только одну копию инструкций функции внутри вашей программы. Каждый раз, когда ваша программа вызывает функцию, компилятор C++ помещает в программу специальные инструкции, которые заносят параметры функции в стек и затем выполняют переход к инструкциям этой функции. Когда операторы функции завершаются, выполнение программы продолжается с первого оператора, который следует за вызовом функции. Помещение аргументов в стек и переход в функцию и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее, чем если бы она размещала те же операторы прямо внутри программы при каждой ссылке на функцию. Например, предположим, что следующая программа CALLBEEP.CPP вызывает функцию show_message, которая указанное число раз выдает сигнал на динамик компьютера и затем выводит сообщение на дисплей:

#include <iostream.b>

void show_message(int count, char *message)

{
int i;
for (i = 0; i < count; i++) cout << '\a';
cout << message << endl;
}

void main(void)

{
show_message(3, "Учимся программировать на языке C++");
show_mes sage(2, "Урок 35");
}
sageАноним 10/05/15 Вск 02:57:40 #104 №92626363 DELETED
Следующая программа NO_CALL.CPP не вызывает функцию show_message. Вместо этого она помещает внутри себя те же операторы функции при каждой ссылке на функцию:

#include <iostream.h>

void main (void)

{
int i;
for (i = 0; i < 3; i++) cout << '\a';
cout << " Учимся программировать на языке C++" << endl;
for (i = 0; i < 2; i++) cout << '\a';
cout << "Урок 35" << endl;
}

Обе программы выполняют одно и то же. Поскольку программа NO_CALL не вызывает функцию show_message, она выполняется немного быстрее, чем программа CALLBEEP. В данном случае разницу во времени выполнения определить невозможно, но, если в обычной ситуации функция будет вызываться 1000 раз, вы, вероятно, заметите небольшое увеличение производительности. Однако программа NO_CALL более запутана, чем ее двойник CALL_BEEP, следовательно, более тяжела для восприятия.

При создании программ вы всегда должны попытаться определить, когда лучше использовать обычные функции, а когда лучше воспользоваться встроенными функциями. Для более простых программ предпочтительно использовать обычные функции. Однако, если вы создаете программу, для которой производительность имеет первостепенное значение, вам следовало бы уменьшить количество вызовов функций. Один из способов уменьшения количества вызовов функций состоит в том, чтобы поместить соответствующие операторы прямо в программу, как только что было сделано в программе NO_CALL. Однако, как вы могли убедиться, замена только одной функции внесла значительную путаницу в программу. К счастью, C++ предоставляет ключевое слово inline, которое обеспечивает лучший способ.

Использование ключевого слова inline

При объявлении функции внутри программы C++ позволяет вам предварить имя функции ключевым словом inline. Если компилятор C++ встречает ключевое слово inline, он помещает в выполнимый файл (машинный язык) операторы этой функции в месте каждого ее вызова. Таким образом, можно улучшить читаемость ваших программ на C++, используя функции, и в то же время увеличить производительность, избегая издержек на вызов функций. Следующая программа INLINE.CPP определяет функции тах и min как inline:

#include <iostream.h>

inline int max(int a, int b)

{
if (a > b) return(a);
else return(b) ;
}

inline int min(int a, int b)

{
if (a < b) return(a);
else return(b);
}

void main(void)

{
cout << "Минимум из 1001 и 2002 равен " << min(1001, 2002) << endl;
cout << "Максимум из 1001 и 2002 равен " << max(1001, 2002) << endl;
}

В данном случае компилятор C++ заменит каждый вызов функции на соответствующие операторы функции. Производительность программы увеличивается без ее усложнения.

О встроенных функциях

Если компилятор C++ перед определением функции встречает ключевое слово inline, он будет заменять обращения к этой функции (вызовы) на последовательность операторов, эквивалентную выполнению функции. Таким образом ваши программы улучшают производительность, избавляясь от издержек на вызов функции и в то же время выигрывая в стиле программы, благодаря использованию функций.
sageАноним 10/05/15 Вск 02:58:01 #105 №92626383 DELETED
Как вы уже знаете, при определении класса вы определяете функции этого класса внутри или вне класса. Например, класс employee определяет свои функции внутри самого класса:

class employee

{
public:
employee(char name, char position, float salary)

{
strcpy(employee::name, name);
strcpy(employee::position, position);
employee::salary = salary;
}

void show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Должность: " << position << endl;
cout << "Оклад: $" << salary << endl;
}

private:
char name [64];
char position[64];
float salary;
};

Размещая подобным образом функции внутри класса, вы тем самым объявляете их встроенными {inline). Если вы создаете встроенные функции класса этим способом, C++ дублирует функцию для каждого создаваемого объекта этого класса, помещая встроенный код при каждой ссылке на метод (функцию) класса. Преимущество такого встроенного кода состоит в увеличении производительности. Недостатком является очень быстрое увеличение объема самого определения класса. Кроме того, включение кода функции в определение класса может существенно запугать класс, делая его элементы трудными для восприятия.

Для улучшения читаемости определений ваших классов вы можете вынести функции из определения класса, как вы обычно и делаете, и разместить ключевое слово inline перед определением функции. Например, следующее определение заставляет компилятор использовать встроенные операторы для функции show_employee:

inline void employee::show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Должность: " << position << endl;
cout << "Оклад: $" << salary << endl;
}

ИСПОЛЬЗОВАНИЕ ОПЕРАТОРОВ ЯЗЫКА АССЕМБЛЕРА

Как вы знаете из урока 1, программисты могут создавать программы, используя широкий спектр языков программирования. Затем компилятор преобразует операторы программы в машинный код (нули и единицы), который понимает компьютер. Каждый тип компьютеров поддерживает промежуточный язык, называемый языком ассемблера, который попадает в категорию между машинным языком и языком программирования, таким как C++.

Язык ассемблера использует другие символы для представления инструкций машинного языка. В зависимости от назначения ваших программ, возможно, вам потребуется выполнить операции низкого уровня, для которых необходимо использовать операторы языка ассемблера. В таких случаях вы можете использовать оператор C++ asm для встраивания операторов языка ассемблера в программу. Большинство создаваемых вами программ не потребуют операторов языка ассемблера. Следующая программа USE_ASM.CPP использует оператор asm, чтобы вставить операторы языка ассемблера, необходимые для озвучивания динамика компьютера в среде MS-DOS:
sageАноним 10/05/15 Вск 02:58:24 #106 №92626402 DELETED

Наконец вернулась способность двигаться... Парализующий эффект со временем пропадает... Осмотрели помещение... Тзу сказала, что это специальная камера для пленников... один вход, сосуд с водой... Особенно ее заинтересовало освещение... исходило от каких-то фосфоресцирующих камней... не природного происхождения... внесли в помещение... время от времени караульные заменяли камни... Муравьям свет не нужен, значит, это предназначалось нам...
Когда мы пришли в себя, муравьи снова принялись изучать нас... Сначала окружили Тзу, затем толкнули меня к Вахру... Тзу предположила, что они хотят заставить нас спариваться... Мы с Вахром сделали это, Тзу не стала... Потом принесли теплокровных и дали нам с Вахром... Тзу отказалась от пищи...
Потом все продолжалось в том же духе... Я отложила яйца, но не подпускала к ним муравьев... Они не пытались приблизиться... Хотели заставить и Тзу... отказалась... Не пожелала давать информацию об Империи врагу...
Стали думать о побеге... Могли подходить к выходу, но дальше не пускали стражники... Через проем мы видели еще одно помещение, через туннель напротив... там машины...
– Командир! – услышал я сигнал Махза.
– Рахм на связи.
– У меня сообщение с шаттла.
– Отставить. Доложишь позже.
Я сосредоточился на Кор, а она продолжала.
– Была видна только часть помещения... Там было что-то вроде Смотрового экрана... Но не объемные изображения, как у нас... мерцающий экран, а к нему вроде как прилеплены фигурки... Дисплей показывал нашу базу и муравейник... вокруг базы плоскостные изображения тзенов... Число тзенов периодически менялось... видимо, они изучали наши защитные сооружения и режим патрулирования... Операторов или пульта управления не видела.
Подготовили побег... Запомнили скорость передвижения муравьев, когда нас тащили в муравейник... Запомнили в темноте количество поворотов и сопоставили эту цифру с приблизительной скоростью... Решили, что сможем найти дорогу наверх. Решили не брать с собой светящиеся камни... выдадут наше местонахождение... Мы с Вахром прикроем Тзу... Хотели, чтобы Ученый добрался до своих...
– Командир! – снова раздался сигнал Махза.
– Рахм слушает.
– Нарушение границы защитной зоны на юго-востоке.
– Кто нарушитель?
– Попрыгунчики, в количестве двадцати штук .
– Идут сюда?
– Выжидают. Дистанция семьдесят пять метров.
– Боевая тревога! – скомандовал я. – Наблюдается скопление попрыгунчиков. Направление юго-восток, дистанция семьдесят пять метров.
Я повернулся к Кор:
– Продолжай.
– Мы попытались бежать... Вахр стал отвлекать внимание странными действиями... метался по камере... катался по полу... Потом подбежал к яйцам и принялся топтать их ногами. Трое стражников кинулись к нему, чтобы утихомирить... Он сопротивлялся... Похоже, они не хотели увечить. Убил одного стражника... Мы с Тзу даже не пошевелились... Двое побежали за подмогой, только один остался у входа...
Там было несколько камней... такого же размера, как мои стальные шарики... Метнула и убила стражника... Мы побежали... Вахр остался и занял позицию у выхода, чтобы задержать погоню...
Бежали в полной темноте... Натыкались на стены... Туннели не охраняются... Налетела на муравья, со спины... убила. Столкнулась с другим, двигавшимся навстречу... схватил меня за руку... Тзу убежала одна... Убила муравья, но потеряла руку... Продолжала бежать...
Получила сигнал от Тзу... Она столкнулась с большой группой муравьев... блокировали выход на поверхность... Она побежала вниз, по другому туннелю... Увела за собой погоню...
Я больше не встретила ни одного противника и выбралась на поверхность... добралась до базы... Несколько муравьев заметили меня и пустились в погоню, но потом повернули обратно...
– Командир! – Снова послышался голос Махза. – Еще одна группа попрыгунчиков на севере, с ними несколько муравьев.
– Вас понял, – телепатировал я.
– Разрешите закончить доклад. – Речь Кор вдруг снова сделалась связной. – Хочу особо отметить мужество Тзу. Она умерла как... – Кор дернулась, и тело ее замерло.
– Махз, – позвал я. – Доложи обстановку.
sageАноним 10/05/15 Вск 02:58:46 #107 №92626419 DELETED
#include <iostream.h>

void main(void)

{
cout << "Сейчас будет звонить!" << endl;
asm

{
MOV AH,2
MOV DL,7
INT 21H
}

cout << "Есть!" << endl;
}

Как видите, используя оператор asm, программа комбинирует C++ и операторы языка ассемблера.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Встроенные функции улучшают производительность ваших программ, уменьшая издержки на вызов функций. Из этого урока вы узнали, как и где использовать встроенные функции в своих программах. Вы также узнали, что иногда вашим программам необходимо использовать язык ассемблера для решения определенных задач. Из урока 36 вы узнаете, как ваши программы могут обратиться к аргументам командной строки, которые пользователь вводит при запуске программы. Однако, прежде чем перейти к уроку 36, убедитесь, что вы освоили следующие основные концепции:

Помещение параметров в стек и переход к функции и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее.
Ключевое слово inline заставляет компилятор C++ заменять вызов функции эквивалентной последовательностью операторов, которые бы выполняла эта функция. Поскольку встроенные операторы избавляют от издержек на вызов функции, программа будет выполняться быстрее.
Если вы используете встроенные функции внутри класса, каждый создаваемый вами объект использует свои собственные встроенные операторы. Обычно все объекты одного и того же класса совместно используют один и тот же код функции.
Ключевое слово asm позволяет вам встраивать операторы языка ассемблера в программы на C++.
sageАноним 10/05/15 Вск 02:59:15 #108 №92626442 DELETED
По мере усложнения ваших программ они будут сохранять и получать информацию, используя файлы. Если вы знакомы с файловыми манипуляциями в языке С, вы сможете использовать подобные методы и в C++. Кроме того, как вы узнаете из этого урока, C++ предоставляет набор классов файловых потоков, с помощью которых можно очень легко выполнять операции ввода и вывода (В/В) с файлами. К концу данного урока вы освоите следующие основные концепции:

Используя выходной файловый поток, вы можете писать информацию в файл с помощью оператора вставки (<<).
Используя входной файловый поток, вы можете читать хранимую в файле информацию с помощью оператора извлечения (>>).
Для открытия и закрытия файла вы используете методы файловых классов.
Для чтения и записи файловых данных вы можете использовать операторы вставки и извлечения, а также некоторые методы файловых классов.
Многие программы, которые вы создадите в будущем, будут интенсивно использовать файлы. Выберите время для экспериментов с программами, представленными в данном уроке. И вы обнаружите, что в C++ выполнять файловые операции очень просто.

ВЫВОД В ФАЙЛОВЫЙ ПОТОК

Из урока 33 вы узнали, что cout представляет собой объект типа ostream (выходной поток). Используя класс ostream, ваши программы могут выполнять вывод в cout с использованием оператора вставки или различных методов класса, например cout.put. Заголовочный файл iostream.h определяет выходной поток cout. Аналогично, заголовочный файл f stream.h определяет класс выходного файлового потока с именем ofstream. Используя объекты класса ofstream, ваши программы могут выполнять вывод в файл. Для начала вы должны объявить объект типа ofstream, указав имя требуемого выходного файла как символьную строку, что показано ниже:

ofstream file_object("FILENAME.EXT");

Если вы указываете имя файла при объявлении объекта типа ofstream, C++ создаст новый файл на вашем диске, используя указанное имя, или перезапишет файл с таким же именем, если он уже существует на вашем диске Следующая программа OUT_FILE.CPP создает объект типа ofstream и затем использует оператор вставки для вывода нескольких строк текста в файл BOOKINFO.DAT:

#include <fstream.h>

void main(void)

{
ofstream book_file("BOOKINFO.DAT");
book_file << "Учимся программировать на языке C++, " << "Вторая редакция" << endl;
book_file << "Jamsa Press" << endl;
book_file << "22.95" << endl;
}

В данном случае программа открывает файл BOOKINFO.DAT и затем записывает три строки в файл, используя оператор вставки. Откомпилируйте и запустите эту программу. Если вы работаете в среде MS-DOS, можете использовать команду TYPE для вывода содержимого этого файла на экран:

С:\> TYPE BOOKINFO.DAT <ENTER>

Учимся программировать на языке C++, Вторая редакция

Jamsa Press

$22.95

Как видите, в C++ достаточно просто выполнить операцию вывода в файл.
sageАноним 10/05/15 Вск 02:59:36 #109 №92626459 DELETED
ЧТЕНИЕ ИЗ ВХОДНОГО ФАЙЛОВОГО ПОТОКА

Только что вы узнали, что, используя класс ofstream, ваши программы могут быстро выполнить операции вывода в файл. Подобным образом ваши программы могут выполнить операции ввода из файла, используя объекты типа ifstream. Опять же, вы просто создаете объект, передавая ему в качестве параметра требуемое имя файла:

ifstream input_file("filename.EXT");

Следующая программа FILE_IN.CPP открывает файл BOOKINFO.DAT, который вы создали с помощью предыдущей программы, и читает, а затем отображает первые три элемента файла:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
ifstream input_file("BOOKINFO.DAT") ;
char one[64], two[64], three[64];
input_file >> one;
input_file >> two;
input_file >> three;
cout << one << endl;
cout << two << endl;
cout << three << endl;
}

Если вы откомпилируете и запустите эту программу, то, вероятно, предположите, что она отобразит первые три строки файла. Однако, подобно cin, входные файловые потоки используют пустые символы, чтобы определить, где заканчивается одно значение и начинается другое. В результате при запуске предыдущей программы на дисплее появится следующий вывод:

С:\> FILE_IN <ENTER>

учимся

программировать

на

Чтение целой строки файлового ввода

Из урока 33 вы узнали, что ваши программы могут использовать cin.getline для чтения целой строки с клавиатуры. Подобным образом объекты типа ifstream могут использовать getline для чтения строки файлового ввода. Следующая программа FILELINE.CPP использует функцию getline для чтения всех трех строк файла BOOKINFO.DAT:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
ifstream input_file("BOOKINFO.DAT");
char one[64], two[64], three [64] ;
input_file.getline(one, sizeof(one)) ;
input_file.get line(two, sizeof(two));
input_file.getline(three, sizeof(three)) ;
cout << one << endl;
cout << two << endl;
cout << three << endl;
}

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

ОПРЕДЕЛЕНИЕ КОНЦА ФАЙЛА

Обычной файловой операцией в ваших программах является чтение содержимого файла, пока не встретится конец файла. Чтобы определить конец файла, ваши программы могут использовать функцию еоf потокового объекта. Эта функция возвращает значение 0, если конец файла еще не встретился, и 1, если встретился конец файла. Используя цикл while, ваши программы могут непрерывно читать содержимое файла, пока не найдут конец файла, как показано ниже:

while (! input_file.eof())

{
// Операторы
}

В данном случае программа будет продолжать выполнять цикл, пока функция eof возвращает ложь (0). Следующая программа TEST_EOF.CPP использует функцию eof для чтения содержимого файла BOOKINFO.DAT, пока не достигнет конца файла:

#include <iostream.h>

#include <fstream.h>

void main (void)

{
ifstream input_file("BOOKINFO.DAT");
char line[64];
while (! input_file.eof())

{
input_file.getline(line, sizeof(line));
cout << line << endl;
}
}

Аналогично, следующая программа WORD_EOF.CPP читает содержимое файла по одному слову за один раз, пока не встретится конец файла:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
ifstream input_file("BOOKINFO.DAT");
char word[64] ;
while (! input_file.eof())

{
input_file >> word;
cout << word << endl;
}
}

И наконец, следующая программа CHAR_EOF.CPP читает содержимое файла по одному символу за один раз, используя функцию get, пока не встретит конец файла:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
ifstream input_file("BOOKINFO.DAT");
char letter;
while (! input_file.eof())

{
letter = input_file.get();
cout << letter;
}
}
sageАноним 10/05/15 Вск 02:59:54 #110 №92626472 DELETED
ПРОВЕРКА ОШИБОК ПРИ ВЫПОЛНЕНИИ ФАЙЛОВЫХ ОПЕРАЦИЙ

Программы, представленные до настоящего момента, предполагали, что во время файловых операций В/В не происходят ошибки. К сожалению, это сбывается не всегда. Например, если вы открываете файл для ввода, ваши программы должны проверить, что файл существует. Аналогично, если ваша программа пишет данные в файл, вам необходимо убедиться, что операция прошла успешно (к примеру, отсутствие места на диске, скорее всего, помешает записи данных). Чтобы помочь вашим программам следить за ошибками, вы можете использовать функцию fail файлового объекта. Если в процессе файловой операции ошибок не было, функция возвратит ложь (0). Однако, если встретилась ошибка, функция fail возвратит истину. Например, если программа открывает файл, ей следует использовать функцию fail, чтобы определить, произошла ли ошибка, как это показано ниже:

ifstream input_file("FILENAME.DAT");
if (input_file.fail())

{
cerr << "Ошибка открытия FILENAME.EXT" << endl;
exit(1);
}

Таким образом, программы должны убедиться, что операции чтения и записи прошли успешно. Следующая программа TEST_ALL.CPP использует функцию fail для проверки различных ошибочных ситуаций:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
char line[256] ;
ifstream input_file("BOOKINFO.DAT") ;
if (input_file.fail()) cerr << "Ошибка открытия BOOKINFO.DAT" << endl;
else

{
while ((! input_file.eof()) && (! input_file.fail()))

{
input_file.getline(line, sizeof(line)) ;
if (! input_file.fail()) cout << line << endl;
}
}
}

ЗАКРЫТИЕ ФАЙЛА, ЕСЛИ ОН БОЛЬШЕ НЕ НУЖЕН

При завершении вашей программы операционная система закроет открытые ею файлы. Однако, как правило, если вашей программе файл больше не нужен, она должна его закрыть. Для закрытия файла ваша программа должна использовать функцию close, как показано ниже:

input_file.close ();

Когда вы закрываете файл, все данные, которые ваша программа писала в этот файл, сбрасываются на диск, и обновляется запись каталога для этого файла.
sageАноним 10/05/15 Вск 03:00:18 #111 №92626493 DELETED
УПРАВЛЕНИЕ ОТКРЫТИЕМ ФАЙЛА

В примерах программ, представленных в данном уроке, файловые операции ввода и вывода выполнялись с начала файла. Однако, когда вы записываете данные в выходной файл, вероятно, вы захотите, чтобы программа добавляла информацию в конец существующего файла. Для открытия файла в режиме добавления вы должны при его открытии указать второй параметр, как показано ниже:

ifstream output_file("FILENAME.EXT", ios::app);

В данном случае параметр ios::app указывает режим открытия файла. По мере усложнения ваших программ они будут использовать сочетание значений для режима открытия файла, которые перечислены в табл. 34.

Таблица 34. Значения режимов открытия.

Режим открытия

Назначение

ios::app
Открывает файл в режиме добавления, располагая файловый указатель в конце файла.

ios::ate Располагает файловый указатель в конце файла.
ios::in Указывает открыть файл для ввода .
ios::nocreate
Если указанный файл не существует, не создавать файл и возвратить ошибку.

ios::noreplace Если файл существует, операция открытия должна быть прервана и должна возвратить ошибку.
ios::out Указывает открыть файл для вывода.
ios::trunc Сбрасывает (перезаписывает) содержим, з существующего файла.
Следующая операция открытия файла открывает файл для вывода, используя режим ios::noreplace, чтобы предотвратить перезапись существующего файла:

ifstream output_file("FIlename.EXT", ios::out | ios::noreplace);
sageАноним 10/05/15 Вск 03:00:38 #112 №92626509 DELETED

Все программы, представленные в данном уроке, выполняли файловые операции над символьными строками. По мере усложнения ваших программ, возможно, вам понадобится читать и писать массивы и структуры. Для этого ваши программы могут использовать функции read и write. При использовании функций read и write вы должны указать буфер данных, в который данные будут читаться или из которого они будут записываться, а также длину буфера в байтах, как показано ниже:

input_file.read(buffer, sizeof(buffer)) ;
output_file.write(buffer, sizeof(buffer));

Например, следующая программа STRU_OUT.CPP использует функцию write для вывода содержимого структуры в файл EMPLOYEE.DAT:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
struct employee

{
char name[64];
int age;
float salary;
} worker = { "Джон Дой", 33, 25000.0 };

ofstream emp_file("EMPLOYEE.DAT") ;
emp_file.write((char ) &worker, sizeof(employee));
}

Функция write обычно получает указатель на символьную строку. Символы (char
) представляют собой оператор приведения типов, который информирует компилятор, что вы передаете указатель на другой тип. Подобным образом следующая программа STRU_IN.CPP использует метод read для чтения из файла информации о служащем:

#include <iostream.h>

#include <fstream.h>

void main(void)

{
struct employee

{
char name [6 4] ;
int age;
float salary;
} worker = { "Джон Дой", 33, 25000.0 };

ifstream emp_file("EMPLOYEE.DAT");
emp_file.read((char *) &worker, sizeof(employee));
cout << worker.name << endl;
cout << worker.age << endl;
cout << worker.salary << endl;
}
sageАноним 10/05/15 Вск 03:01:01 #113 №92626529 DELETED
По мере усложнения ваших программ вы регулярно будете использовать файловые операции. Выберите время для экспериментов с программами, представленными в этом уроке. Из урока 35 вы узнаете, как улучшить производительность ваших программ с использованием встроенных функций. Прежде чем перейти к уроку 35, убедитесь, что вы изучили следующее:

Заголовочный файл fstream.h определяет классы ifstream и ofstream, с помощью которых ваша программа может выполнять операции файлового ввода и вывода.
Для открытия файла на ввод или вывод вы должны объявить объект типа ifstream или ofstream, передавая конструктору этого объекта имя требуемого файла.
После того как ваша программа открыла файл для ввода или вывода, она может читать или писать данные, используя операторы извлечения (>>) и вставки (<<).
Ваши программы могут выполнять ввод или вывод символов в файл или из файла, используя функции get и put.
Ваши программы могут читать из файла целую строку, используя функцию getline.
Большинство программ читают содержимое файла, пока не встретится конец файла. Ваши программы могут определить конец файла с помощью функции eof.
Когда ваши программы выполняют файловые операции, они должны проверять состояние всех операций, чтобы убедиться, что операции выполнены успешно. Для проверки ошибок ваши программы могут использовать функцию fail.
Если вашим программам необходимо вводить или выводить такие данные, как структуры или массивы, они могут использовать методы read и write.
Если ваша программа завершила работу с файлом, его следует закрыть с помощью функции close.
sageАноним 10/05/15 Вск 03:01:29 #114 №92626556 DELETED
Цель объектно-ориентированного программирования состоит в повторном использовании созданных вами классов, что экономит ваше время и силы. Если вы уже создали некоторый класс, то возможны ситуации, что новому классу нужны многие или даже все особенности уже существующего класса, и необходимо добавить один или несколько элементов данных или функций. В таких случаях C++ позволяет вам строить новый объект, используя характеристики уже существующего объекта. Другими словами, новый объект будет наследовать элементы существующего класса (называемого базовым классом). Когда вы строите новый класс из существующего, этот новый класс часто называется производным классом. В этом уроке впервые вводится наследование классов в C++ . К концу данного урока вы изучите следующие основные концепции:

Ели ваши программы используют наследование, то для порождения нового класса необходим базовый класс, т.е. новый класс наследует элементы базового класса.
Для инициализации элементов производного класса ваша программа должна вызвать конструкторы базового и производного классов.
Используя оператор точку, программы могут легко обращаться к элементам базового и производного классов.
В дополнение к общим (public) (доступным всем) и частным (private) (доступным методам класса) элементам C++ предоставляет защищенные (protected) элементы, которые доступны базовому и производному классам.
Для разрешения конфликта имен между элементами базового и производного классов ваша программа может использовать оператор глобального разрешения, указывая перед ним имя базового или производного класса.
Наследование является фундаментальной концепцией объектно-ориентированного программирования. Выберите время для экспериментов с программами, представленными в этом уроке. И вы обнаружите, что реально наследование реализуется очень просто и может сохранить огромные усилия, затрачиваемые на программирование.
sageАноним 10/05/15 Вск 03:01:32 #115 №92626559 
>>92625057
Лолблять, зарегестрировался, залогинился, зашел. Что сложного?
sageАноним 10/05/15 Вск 03:01:50 #116 №92626576 DELETED
ПРОСТОЕ НАСЛЕДОВАНИЕ

Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Например, предположим, что у вас есть базовый класс employee:

class employee

{
public:
employee(char , char , float);
void show_employee(void);
private:
char name[64];
char position[64];
float salary;
};

Далее предположим, что вашей программе требуется класс manager, который добавляет следующие элементы данных в класс employee:

float annual_bonus;
char company_car[64];
int stock_options;

В данном случае ваша программа может выбрать два варианта: во-первых, программа может создать новый класс manager, который дублирует многие элементы класса employee, или программа может породить класс типа manager из базового класса employee. Порождая класс manager из существующего класса employee, вы снижаете объем требуемого программирования и исключаете дублирование кода внутри вашей программы.

Для определения этого класса вы должны указать ключевое слово class, имя manager, следующее за ним двоеточие и имя employee, как показано ниже:

Производный класс //-----> class manager : public employee { <-------// Базовый класс

// Здесь определяются элементы
};

Ключевое слово public, которое предваряет имя класса employee, указывает, что общие (public) элементы класса employee также являются общими и в классе manager. Например, следующие операторы порождают класс manager.

class manager : public employee

{
public:
manager(char , char , char , float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;
};

Когда вы порождаете класс из базового класса, частные элементы базового класса доступны производному классу только через интерфейсные функции базового класса. Таким образом, производный класс не может напрямую обратиться к частным элементам базового класса, используя оператор точку.

Следующая программа MGR_EMP.CPP иллюстрирует использование наследования в C++ , создавая класс manager из базового класса employee:

#include <iostream.h>

#include <string.h>

class employee

{
public:
employee(char
, char , float);
void show_employee(void);
private:
char name [ 64 ];
char position[64];
float salary;
};

employee::employee(char
name, char position,float salary)

{
strcpy(employee::name, name);
strcpy(employee::position, position);
employee::salary = salary;
}

void employee::show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Должность: " << position << endl;
cout << "Оклад: $" << salary << endl;
}

class manager : public employee

{
public:
manager(char
, char , char , float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;
};

manager::manager(char name, char position, char company_car, float salary, float bonus, int stock_options) : employee(name, position, salary)

{
strcpy(manager::company_car, company_car) ;
manager::annual_bonus = bonus ;
manager::stock_options = stock_options;
}

void manager::show_manager(void)

{
show_employee();
cout << "Машина фирмы: " << company_car << endl;
cout << "Ежегодная премия: $" << annual_bonus << endl;
cout << "Фондовый опцион: " << stock_options << endl;
}

void main(void)

{
employee worker("Джон Дой", "Программист", 35000);
manager boss("Джейн Дой", "Вице-президент ", "Lexus", 50000.0, 5000, 1000);
worker.show_employee() ;
boss.show_manager();
}

Как видите, программа определяет базовый класс employee, а затем определяет производный класс manager. Обратите внимание на конструктор manager. Когда вы порождаете класс из базового класса, конструктор производного класса должен вызвать конструктор базового класса. Чтобы вызвать конструктор базового класса, поместите двоеточие сразу же после конструктора производного класса, а затем укажите имя конструктора базового класса с требуемыми параметрами:

manager::manager(char
name, char position, char company_car, float salary, float bonus, int stock_options) :
employee(name, position, salary) //————————————— Конструктор базового класса

{
strcpy(manager::company_car, company_car);
manager::annual_bonus = bonus;
manager::stock_options = stock_options;
}

Также обратите внимание, что функция show_manager вызывает функцию show_employee, которая является элементом класса employee. Поскольку класс manager является производным класса employee, класс manager может обращаться к общим элементам класса employee, как если бы все эти элементы были определены внутри класса manager,

Представление о наследовании

Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Простыми словами это означает, что, если у вас есть класс, чьи элементы данных или функции-элементы могут быть использованы новым классом, вы можете построить этот новый класс в терминах существующего (или базового) класса. Новый класс в свою очередь будет наследовать элементы (характеристики) существующего класса. Использование наследования для построения новых классов сэкономит вам значительное время и силы на программирование. Объектно-ориентированное программирование широко использует наследование, позволяя вашей программе строить сложные объекты из небольших легко управляемых объектов.
sageАноним 10/05/15 Вск 03:02:09 #117 №92626595 DELETED
Второй пример

Предположим, например, что вы используете следующий базовый класс book внутри существующей программы:

class book

{
public:
book (char , char , int);
void show_book(void);
private:
char title[64];
char author[б 4];
int pages;
};

Далее предположим, что программе требуется создать класс library_card, который будет добавлять следующие элементы данных в класс book:

char catalog[64];
int checked_out; // 1, если проверена, иначе О

Ваша программа может использовать наследование, чтобы породить класс library _card из класса book, как показано ниже:

class library_card : public book

{
public:
library_card(char , char , int, char , int);
void show_card(void);
private:
char catalog[64] ;
int checked_out;
};

Следующая программа BOOKCARD.CPP порождает класс library_card из клacca book:

#include <iostream.h>

#include <string.h>

class book

{
public:
book(char
, char , int);
void show_book(void);
private:
char title [64];
char author[64];
int pages;
};

book::book(char •title, char
author, int pages)

{
strcpy(book::title, title);
strcpy(book::author, author);
book::pages = pages;
}

void book::show_book(void)

{
cout << "Название: " << title << endl;
cout << "Автор: " << author << endl;
cout << "Страниц: " << pages << endl;
}

class library_card : public book

{
public:
library_card(char , char , int, char , int);
void show_card(void) ;
private:
char catalog[64];
int checked_out;
};

library_card::library_card(char
title, char author, int pages, char catalog, int checked_out) : book(title, author, pages)

{
strcpy(library_card::catalog, catalog) ;
library_card::checked_out = checked_out;
}

void 1ibrary_card::show_card(void)

{
show_book() ;
cout << "Каталог: " << catalog << endl;
if (checked_out) cout << "Статус: проверена" << endl;
else cout << "Статус: свободна" << endl;
}

void main(void)

{
library_card card( "Учимся программировать на языке C++", "Jamsa", 272, "101СРР", 1);
card.show_card();
}

Как и ранее, обратите внимание, что конструктор library _card вызывает конструктор класса book для инициализации элементов класса book. Кроме того, обратите внимание на использование функции-элемента show_book класса book внутри функции show_card. Поскольку класс library_card наследует методы класса book, функция show_card может вызвать этот метод (show_book) без помощи оператора точки, как если бы этот метод был методом класса library _card.

ЧТО ТАКОЕ ЗАЩИЩЕННЫЕ ЭЛЕМЕНТЫ

При изучении определений базовых классов вы можете встретить элементы, объявленные как public, private и protected (общие, частные и защищенные). Как вы знаете, производный класс может обращаться к общим элементам базового класса, как будто они определены в производном классе. С другой стороны, производный класс не может обращаться к частным элементам базового класса напрямую. Вместо этого для обращения к таким элементам производный класс должен использовать интерфейсные функции. Защищенные элементы базового класса занимают промежуточное положение между частными и общими. Если элемент является защищенным, объекты производного класса могут обращаться к нему, как будто он является общим. Для оставшейся части вашей программы защищенные элементы являются как бы частными. Единственный способ, с помощью которого ваши программы могут обращаться к защищенным элементам, состоит в использовании интерфейсных функций. Следующее определение класса book использует метку protected, чтобы позволить классам, производным от класса book, обращаться к элементам title, author и pages напрямую, используя оператор точку:
sageАноним 10/05/15 Вск 03:02:29 #118 №92626612 DELETED
class book

{
public:
book(char , char , int) ;
void show_book(void) ;
protected:
char title [64];
char author[64];
int pages;
};

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

Защищенные элементы обеспечивают доступ и защиту

Как вы уже знаете, программа не может обратиться напрямую к частным элементам класса. Для обращения к частным элементам программа должна использовать интерфейсные функции, которые управляют доступом к этим элементам. Как вы, вероятно, заметили, наследование упрощает программирование в том случае, если производные классы могут обращаться к элементам базового класса с помощью оператора точки. В таких случаях ваши программы могут использовать защищенные элементы класса. Производный класс может обращаться к защищенным элементам базового класса напрямую, используя оператор точку. Однако оставшаяся часть вашей программы может обращаться к защищенным элементам только с помощью интерфейсных функций этого класса. Таким образом, защищенные элементы класса находятся между общими (доступными всей программе) и частными (доступными только самому классу) элементами.

РАЗРЕШЕНИЕ КОНФЛИКТА ИМЕН

Если вы порождаете один класс из другого, возможны ситуации, когда имя элемента класса в производном классе является таким же, как имя элемента в базовом классе. Если возник такой конфликт, C++ всегда использует элементы производного класса внутри функций производного класса. Например, предположим, что классы book и library_card используют элемент price. В случае класса book элемент price соответствует продажной цене книги, например $22.95. В случае класса library'_card price может включать библиотечную скидку, например $18.50. Если в вашем исходном тексте не указано явно (с помощью оператора глобального разрешения), функции класса library_card будут использовать элементы производного класса {library_card). Если же функциям класса library_card необходимо обращаться к элементу price базового класса {book), они должны использовать имя класса book и оператор разрешения, например book::price. Предположим, что функции show_card необходимо вывести обе цены. Тогда она должна использовать следующие операторы:

cout << "Библиотечная цена: $" << price << endl;
cout << "Продажная цена: $" << book::price << endl;

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Из этого урока вы узнали, что наследование в C++ позволяет вам строить /порождать) новый класс из существующего класса. Строя таким способом один класс из другого, вы уменьшаете объем программирования, что, в свою очередь, экономит ваше время. Из урока 27 вы узнаете, что C++ позволяет вам порождать класс из двух или нескольких базовых классов. Использование нескольких базовых классов для порождения класса представляет собой множественное наследование. До изучения урока 27 убедитесь, что освоили следующие основные концепции:

Наследование представляет собой способность производить новый класс из существующего базового класса.
Производный класс — это новый класс, а базовый класс — существующий класс.
Когда вы порождаете один класс из другого (базового класса), производный класс наследует элементы базового класса.
Для порождения класса из базового начинайте определение производного класса ключевым словом class, за которым следует имя класса, двоеточие и имя базового класса, например class dalmatian: dog.
Когда вы порождаете класс из базового класса, производный класс может обращаться к общим элементам базового класса, как будто эти элементы определены внутри самого производного класса. Для доступа к частным данным базового класса производный класс должен использовать интерфейсные функции базового класса.
Внутри конструктора производного класса ваша программа должна вызвать конструктор базового класса, указывая двоеточие, имя конструктора базового класса и соответствующие параметры сразу же после заголовка конструктора производного класса.
Чтобы обеспечить производным классам прямой доступ к определенным элементам базового класса, в то же время защищая эти элементы от оставшейся части программы, C++ обеспечивает защищенные {protected) элементы класса. Производный класс может обращаться к защищенным элементам базового класса, как будто они являются общими. Однако для оставшейся части программы защищенные элементы эквивалентны частным.
Если в производном и базовом классе есть элементы с одинаковым именем, то внутри функций производного класса C++ будет использовать элементы производного класса. Если функциям производного класса необходимо обратиться к элементу базового класса, вы должны использовать оператор глобального разрешения, например base class:: member.
sageАноним 10/05/15 Вск 03:03:00 #119 №92626642 DELETED
Из урока 18 вы узнали, как группировать связанную информацию в одной переменной с помощью структур C++. По мере усложнения вашим программам могут потребоваться разные способы просмотра части информации. Кроме того, программе может потребоваться работать с двумя или несколькими значениями, используя при этом только одно значение в каждый момент времени. В таких случаях для хранения данных ваши программы могут использовать объединения. В данном уроке вы изучите, как создавать и использовать объединения для хранения информации. Как вы узнаете, объединения очень похожи на структуры, описанные в уроке 18. Прежде чем вы закончите этот урок, вы освоите следующие основные концепции:

Объединения C++ очень похожи на структуры, за исключением того, как C++ хранит их в памяти; кроме того, объединение может хранить значение только для одного элемента в каждый момент времени.
Объединение представляет собой структуру данных, подобную структуре C++, и состоит из частей, называемых элементами.
Объединение определяет шаблон, с помощью которого программы далее объявляют переменные.
Для обращения к определенному элементу объединения ваши программы используют оператор C++ точку.
Чтобы изменить значения элемента объединения внутри функции, ваша программа должна передать переменную объединения в функцию с помощью адреса.
Анонимное объединение представляет собой объединение, у которого нет имени (тэга).
Как вы узнаете, объединения очень похожи на структуры C++, однако способ, с помощью которого C++ хранит объединения, отличается от способа, с помощью которого C++ хранит структуры.

КАК C++ ХРАНИТ ОБЪЕДИНЕНИЯ

Внутри ваших программ объединения C++ очень похожи на структуры. Например, следующая структура определяет объединение с именем distance, содержащее два элемента:

union distance

{
int miles;
long meters;
};

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

union distance

{
union distance

{
int miles; int miles;
long meters; long meters;
} japan, germany, franee;
};
distance japan, germany, franee;

Как видите, данное объединение содержит два элемента: miles и meters. Эти объявления создают переменные, которые позволяют вам хранить расстояния до указанных стран. Как и для структуры, ваша программа может присвоить значение любому элементу. Однако в отличие от структуры значение может быть присвоено только одному элементу в каждый момент времени. Когда вы объявляете объединение, компилятор C++ распределяет память для хранения самого большого элемента объединения. В случае объединения distance компилятор распределяет достаточно памяти для хранения значения типа long, как показано на рис. 19.



Рис. 19. C++ распределяет память, достаточную для хранения только самого большого элемента объединения.

Предположим, что ваша программа присваивает значение элементу miles, как показано ниже:
sageАноним 10/05/15 Вск 03:03:20 #120 №92626662 DELETED
japan.miles = 12123;

Если далее ваша программа присваивает значение элементу meters, значение, присвоенное элементу miles, теряется.

Следующая программа USEUNION.CPP иллюстрирует использование объединения distance. Сначала программа присваивает значение элементу

miles и выводит это значение. Затем программа присваивает значение элементу meters. При этом значение элемента miles теряется:

#include <iostream.h>

void main(void)

{
union distance

{
int miles;
long meters;
} walk;

walk.miles = 5;
cout << "Пройденное расстояние в милях " << walk.miles << endl;
walk.meters = 10000;
cout << "Пройденное расстояние в метрах " << walk.meters << endl;
}

Как видите, программа обращается к элементам объединения с помощью точки, аналогичная запись использовалась при обращении к элементам структуры в уроке 18.

Объединение хранит значение только одного элемента в каждый момент времени

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

Объединение определяет шаблон, с помощью которого ваши программы могут позднее объявлять переменные. Когда компилятор C++ встречает определение объединения, он распределяет количество памяти, достаточное для хранения только самого большого элемента объединения.

ПРЕДСТАВЛЕНИЕ ОБ АНОНИМНЫХ ОБЪЕДИНЕНИЯХ C++

Анонимное объединение представляет собой объединение, у которого нет имени. C++ предоставляет анонимные объединения, чтобы упростить использование элементов объединений, предназначенных для экономии памяти или создания псевдонимов для определенного значения. Например, предположим, что вашей программе требуются две переменные miles и meters. Кроме того, предположим, что программа использует только одну из них каждый данный момент времени. В этом случае программа могла бы использовать элементы объединения, подобного уже обсуждавшемуся объединению distance, а именно name.miles и name.meters. Следующий оператор создает анонимное (безымянное) объединение:
sageАноним 10/05/15 Вск 03:03:44 #121 №92626677 DELETED
union

{
int miles;
long meters;
};

Как видите, объявление не использует имя объединения и не объявляет переменную объединения. Программа, в свою очередь, может обращаться к элементам с именами miles и meters без помощи точки. Следующая программа ANONYM.CPP создает анонимное объединение, которое содержит элементы miles и meters. Обратите внимание, что программа трактует элементы как обычные переменные. Однако различие между элементами и обычными переменными заключается в том, что, когда вы присваиваете значение любому из этих элементов, значение другого элемента теряется:

#include <iostream.h>

void main(void)

{
union

{
int miles;
long meters;
};

miles = 10000;
cout << "Значение в милях " << miles << endl;
meters = 150000;
cout << "Значение в метрах " << meters << endl;
}

Как видите, с помощью анонимного объединения, программа может сэкономить память, не используя имя объединения и точку для обращения к значениям элементов.

Анонимные объединения позволяют вашим программам экономить пространство

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

union

{
char short_name[13];
char long_name[255];
};

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Из этого урока вы узнали, как создать объединение внутри вашей программы. Вы уже поняли, что формат объединения подобен формату структуры. Однако способ, с помощью которого C++ хранит объединения, очень отличается от способа хранения структуры. Из урока 10 вы впервые узнали, что для того, чтобы функция изменила параметр, вашей программе следует передать этот параметр в функцию с помощью указателя (или адреса памяти). Начиная с десятого урока, ваши программы использовали указатели для массивов и символьных строк. В уроке 20 вы рассмотрите операции с указателями C++ с другой стороны. До изучения урока 20 убедитесь, что вы освоили следующее:
sageАноним 10/05/15 Вск 03:04:23 #122 №92626720 DELETED
Когда вы объявляете объединение, компилятор C++ распределяет память, достаточную для хранения только самого большого элемента объединения.
Описание объединения не распределяет память, вместо этого оно обеспечивает шаблон, с помощью которого программы могут позднее объявлять переменные.
Программы обращаются к элементам объединения, используя точку. Когда ваша программа присваивает значение элементу объединения, то значение, присвоенное, возможно, ранее другому элементу, теряется.
Анонимное объединение представляет собой объединение, у которого нет имени. Когда программа объявляет анонимное объединение, она может использовать элементы такого объединения подобно любым другим переменным без точки.
Аноним 10/05/15 Вск 03:04:44 #123 №92626739 
Бамп назло школьнику.

мимопроходил, тред не читал
sageАноним 10/05/15 Вск 03:04:46 #124 №92626741 DELETED
ак вы уже знаете, ваши программы во время выполнения хранят информацию в переменных. До сих пор каждая переменная в программе хранила только одно значение в каждый момент времени. Однако в большинстве случаев программам необходимо хранить множество значений, например 50 тестовых очков, 100 названий книг или 1000 имен файлов. Если вашим программам необходимо хранить несколько значений, они должны использовать специальную структуру данных, называемую массивом. Для объявления массива необходимо указать имя, тип массива и количество значений, которые массив будет хранить. Этот урок описывает, как программы объявляют массивы, а затем сохраняют и обращаются к информации, содержащейся в массиве. К концу данного урока вы освоите следующие основные концепции:

Массив представляет собой структуру данных, которая позволяет одной переменной хранить несколько значений.
При объявлении массива вы должны указать тип значений, хранящихся в массиве, а также количество значений (называемых элементами массива).
Все элементы внутри массива должны быть одного и того же типа, например, int, float или char.
Для сохранения значения внутри массива вам следует указать номер элемента массива, в котором вы хотите сохранить свое значение.
Чтобы обратиться к значению, хранящемуся внутри массива, ваши программы указывают имя массива и номер элемента.
При объявлении массива программы могут использовать оператор присваивания для инициализации элементов массива.
Программы могут передавать переменные-массивы в функции точно так же, как они передают любой другой параметр.
Программы на C++ широко используют массивы. Когда в уроке 17 вы начнете работать с символьными строками (например, название книги, имя файла и т. д.), вы будете оперировать массивами символов.

ОБЪЯВЛЕНИЕ ПЕРЕМЕННОЙ МАССИВА

Массив представляет собой переменную, способную хранить одно или несколько значений. Подобно переменным, используемым вашими программами до сих пор, массив должен иметь тип (например, inl, char или float) и уникальное имя. В дополнение к этому вам следует указать количество значений, которые массив будет хранить. Все сохраняемые в массиве значения должны быть одного и того же типа. Другими словами, ваша программа не может поместить значения типа float, char и long в один и тот же массив. Следующее объявление создает массив с именем test_scores, который может вмещать 100 целых значений для тестовых очков:

———————————————— Тип массива

int test_scores [100] ; //------> Размер массива

Когда компилятор C++ встречает объявление этой переменной, он распределит достаточно памяти для хранения 100 значений типа int. Значения, хранящиеся в массиве, называются элементами массива.
sageАноним 10/05/15 Вск 03:05:12 #125 №92626758 DELETED
Массивы хранят несколько значений одного и того же типа

По мере усложнения вашим программам потребуется работать с несколькими значениями одного и того же типа. Например, программы могут хранить возраст 100 служащих или стоимость 25 акций. Вместо того чтобы заставлять программу работать со 100 или с 25 переменными с уникальными именами, C++ позволяет вам определить одну переменную — массив —, которая может хранить несколько связанных значений.

Для объявления массива вы должны указать тип и уникальное имя массива, а также количество элементов, которые будет содержать массив. Например, следующие операторы объявляют три разных массива:

float part_cost[50];
int employee_age[100];
float stock_prices[25];

Обращение к элементам массива

Как вы уже знаете, массив позволяет вашим программам хранить несколько значений в одной и той же переменной. Для обращения к определенным значениям, хранящимся в массиве, используйте значение индекса, которое указывает на требуемый элемент. Например, для обращения к первому элементу массива test_scores вы должны использовать значение индекса 0. Для обращения ко второму элементу используйте индекс 1. Подобно этому, для обращения к третьему элементу используйте индекс 2. Как показано на рис. 16.1, первый элемент массива всегда имеет индекс 0, а значение индекса последнего элемента на единицу меньше размера массива:



Рис. 16.1. Как C++ индексирует элементы массива.

Важно помнить, что C++ всегда использует 0 для индекса первого элемента массива, а индекс последнего элемента на единицу меньше размера массива. Следующая программа ARRAY. CPP создает массив с именем values, который вмещает пять целочисленных значений. Далее программа присваивает элементам значения 100, 200, 300, 400 и 500:

#include <iostream.h>

void main(void)

{
int values[5]; // Объявление массива
values[0] = 100;
values[1] = 200;
values[2] = 300;
values[3] = 400;
values [4] = 500;
cout << "Массив содержит следующие значения" << endl;
cout << values [0] << ' ' << values [1] << ' ' << values [2] << ' ' << values [3] << ' ' << values [4] << endl;
}

Как видите, программа присваивает первое значение элементу 0 (va lues[0]). Она также присваивает последнее значение элементу 4 (размер Массива (5) минус 1).

Использование индекса для обращения к элементам массива

Массив позволяет вашим программам хранить несколько значений внутри одной и той же переменной. Для обращения к определенному значению внутри массива программы используют индекс. Говоря кратко, значение индекса указывает требуемый элемент массива. Все массивы C++ начинаются с элемента с индексом 0. Например, следующий оператор присваивает значение 100 первому элементу массива с именем scores:

scores[0] = 100;

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

int scores[100];

В данном случае массив представляет собой элементы от scores[0] до scores[99].

Использование индексной переменной

Если ваши программы используют массив, обычной операцией является использование индексной переменной для обращения к элементам массива. Например, предположим, что переменная / содержит значение 3, следующий оператор присваивает значение 400 элементу values[3J:

values = 400;

Следующая программа SHOWARRA.CPP использует индексную переменную i внутри цикла for для вывода элементов массива. Цикл for инициализирует i нулем, так что программа может обращаться к элементу values. Цикл for завершается, когда i больше 4 (последний элемент массива):

#include <iostream.h>

void main (void)

{
int values[5]; // Объявление массива int i;
values[0] = 100;
values[1] = 200;
values[2] = 300;
values[3] = 400;
values[4] = 500;
cout << "Массив содержит следующие значения" << endl;
for (i = 0; i < 5; i++) cout << values << ' ';
}

Каждый раз, когда цикл for увеличивает переменную i, программа может обратиться к следующему элементу массива. Экспериментируйте с этой программой, изменяя цикл for следующим образом:

for (i = 4; i >= 0; i--) cout << values << ' ';

В данном случае программа будет выводить элементы массива от большего к меньшему.

ИНИЦИАЛИЗАЦИЯ МАССИВА ПРИ ОБЪЯВЛЕНИИ
sageАноним 10/05/15 Вск 03:05:31 #126 №92626775 DELETED
Как вы уже знаете, C++ позволяет вашим программам инициализировать переменные при объявлении. То же верно и для массивов. При объявлении массива вы можете указать первоначальные значения, поместив их между левой и правой фигурными скобками, следующими за знаком равенства. Например, следующий оператор инициализирует массив values:

int values[5] = { 100, 200, 300, 400, 500 };

Подобным образом следующее объявление инициализирует массив с плавающей точкой:

float salaries[3] = { 25000.00. 35000.00, 50000.00 };

Если вы не указываете первоначальное значение для какого-либо элемента массива, большинство компиляторов C++ будут инициализировать такой элемент нулем. Например, следующее объявление инициализирует первые три из пяти элементов массива:

int values[5] = { 100, 200, 300 };

Программа не инициализирует элементы values[3] и values[4]. В зависимости от вашего компилятора, эти элементы могут содержать значение 0. Если вы не указываете размер массива, который вы инициализируете при объявлении, C++ распределит достаточно памяти, чтобы вместить все определяемые элементы. Например, следующее объявление создает массив, способяый хранить четыре целочисленных значения:

int numbers[] = { 1, 2, 3, 4 };

ПЕРЕДАЧА МАССИВОВ В ФУНКЦИИ

Ващи программы будут передавать массивы в функции точно так же, как и любые другие переменные. Функция может инициализировать массив, прибавить к массиву значения или вывести элементы массива на экран. Когда вы передаете массив в функцию, вы должны указать тип массива. Нет необходимости указывать размер массива. Вместо этого вы передаете параметр например number_of_elements, который содержит количество элементов в массиве:

void some_function(int array[], int number_of_elements);

Следующая программа ARRAYFUN.CPP передает массивы в функцию show_array, которая использует цикл for для вывода значений массивов:

#include <iostream.h>

void show_array (int array [] , int number_of_elements)

{
int i;
for (i = 0; i < number_of_elements; i++) cout << array << ' ';
cout << endl;
}

void main(void)

{
int little_numbers[5] ={1,2,3,4,5};
int big_numbers[3] = { 1000, 2000, 3000 };
show_array(little_numbers, 5);
show_array(big_numbers, 3);
}

Как видите, программа просто передает массив в функцию по имени, а также указывает параметр, который сообщает функции количество элементов, содержащихся в массиве:

show_array(little_numbers, 5);

Следующая программа GETARRAY.CPP использует функцию get_values, ч тобы присвоить три значения массиву numbers:

#include <iostream.h>

void get_values(int array[], int number_of_elements)

{
int i;
for (i = 0; i < number_of_elements; i++)

{
cout “ "Введите значение " << i << ": ";
cin ” array ;
}
}

void main(void)

{
int numbers[3];
get_values(numbers, 3);
cout << "Значения массива" << endl;
for (int i = 0; i < 3; i++)
cout << numbers << endl;
}

Как видите, программа передает массив в функцию по имени. Функция в свою очередь присваивает массиву элементы. Из урока 10 вы узнали, что, пока ваша программа не передаст параметры в функцию с помощью адреса, функция не может изменить эти параметры. Однако, как можно заметить в данном случае, функция get_values изменяет параметр-массив numbers. Как вы узнаете из урока 20, C++ действительно передает массивы в функцию, используя указатели. Таким образом, функция может изменить элементы массива, если ей это нужно.
sageАноним 10/05/15 Вск 03:05:51 #127 №92626788 DELETED
Из этого урока вы узнали, что ваши программы могут хранить несколько значений одного и того же типа внутри массива. Программы на C++ широко используют массивы. Из урока 17 вы узнаете, что программы могут использовать массивы для хранения символьных строк. До изучения урока 17 убедитесь, что вы освоили следующие основные концепции:

Массив представляет собой переменную, которая может хранить одно или несколько значений одного и того же типа.
Для объявления массива вам следует указать тип, имя массива, а также количество значений, хранящихся в массиве.
Значения внутри массива называются элементами массива.
Первый элемент массива хранится как элемент 0 (array[OJ), индекс последнего элемента массива на единицу меньше размера массива.
Программы часто используют индексные переменные для обращения к элементам массива.
Если функция воспринимает массив как параметр, она должна указать тип и имя, но не размер массива.
Если программа передает массив в функцию, она, как правило, передает и параметр, который сообщает функции количество элементов содержащихся в массиве.
Так как C++ передает массив в функцию с помощью адреса массива функция может изменять значения, содержащиеся в массиве.
sageАноним 10/05/15 Вск 03:06:38 #128 №92626820 DELETED
Как вы уже знаете, ваши программы могут обращаться к частным (private) элементам класса только с помощью функций-элементов этого же класса. Используя частные элементы класса вместо общих во всех ситуациях, где это только возможно, вы уменьшаете возможность программы испортить значения элементов класса, так как программа может обращаться к таким элементам только через интерфейсные функции (которые управляют доступом к частным элементам). Однако в зависимости от использования объектов вашей программы, иногда вы можете существенно увеличить производительность позволяя одному классу напрямую обращаться к частным элементам другого. В этом случае уменьшаются издержки (требуемое время выполнения) на вызов интерфейсных функций. В подобных ситуациях C++ позволяет определить класс в качестве друга (friend} другого класса и разрешает классу-другу доступ к частным элементам этого другого класса. В этом уроке объясняется, как ваши программы могут указать, что два класса являются друзьями. К концу данного урока вы освоите следующие основные концепции:

Используя ключевое слово friend, класс может сообщить C++, кто является его другом, т. е. другими словами, что другие классы могут обращаться напрямую к его частным элементам.
Частные элементы класса защищают данные класса, следовательно, вы должны ограничить круг классов-друзей только теми классами, которым действительно необходим прямой доступ к частным элементам искомого класса.
C++ позволяет ограничить дружественный доступ определенным набором функций.
Частные (private) элементы позволяют вам защищать классы и уменьшить вероятность ошибок. Таким образом, вы должны ограничить использование классов-друзей настолько, насколько это возможно. Иногда программа напрямую может изменить значения элементов класса, это увеличивает вероятность появления ошибок.

ОПРЕДЕЛЕНИЕ ДРУЗЕЙ КЛАССА

C++ позволяет друзьям определенного класса обращаться к частным элементам этого класса. Чтобы указать C++, что один класс является другом (friend) другого класса, вы просто помещаете ключевое слово friend и имя соответствующего класса-друга внутрь определения этого другого класса. Например, приведенный ниже класс book объявляет класс librarian своим другом. Поэтому объекты класса librarian могут напрямую обращаться к частным элементам класса book, используя оператор точку:

class book

{
public:
book (char , char , char );
void show_book(void);
friend librarian;
private:
char title [64] ;
char author[64];
char catalog[64];
};

Как видите, чтобы указать друга, необходим только один оператор внутри определения класса. Например, следующая программа VIEWBOOK.CPP использует librarian в качестве друга класса book. Следовательно, функции класса librarian могут напрямую обращаться к частным элементам класса book. В данном случае программа использует функцию change_catalog класса librarian для изменения номера карточки каталога определенной книги:

#include <iostream.h>

#include <string.h>

class book

{
public:
book (char
, char , char );
void show_book(void);
friend librarian;
private:
char title[64] ;
char author[64];
char catalog[64];
};

book::book(char title, char author, char •catalog)

{
strcpy(book::title, title);
strcpy(book::author, author) ;
strcpy(book::catalog, catalog);
}

void book::show_book(void)

{
cout << "Название: " << title << endl;
cout << "Автор: " << author << endl;
cout << "Каталог: " << catalog << endl;
}
Аноним 10/05/15 Вск 03:06:53 #129 №92626835 
Дауну бомбануло так, что он вручную сагает, найс.
Самое смешное, что это наверное тот же ишак, которые говорил, что у него времени нет.
sageАноним 10/05/15 Вск 03:06:55 #130 №92626837 DELETED
class librarian

{
public:
void change_catalog(book , char );
char get_catalog(book);
};

void librarian::change_catalog(book
this_book, char new_catalog)

{
strcpy(this_book->catalog, new_catalog);
}

char
librarian: :get__catalog(book this_book)

{
static char catalog[64];
strcpy(catalog, this_book.catalog);
return(catalog) ;
}

void main(void)

{
book programming( "Учимся программировать на языке C++", "Jamsa", "P101");
librarian library;
programming.show_book();
library.change_catalog(&programming, "Легкий C++ 101");
programming.show_book();
}

Как видите, программа передает объект book в функцию change_catalog класса librarian по адресу. Поскольку эта функция изменяет элемент класса book, программа должна передать параметр по адресу, а затем использовать указатель для обращения к элементу этого класса. Экспериментируйте с данной программой, попробуйте удалить оператор friend из определения класса book. Поскольку класс librarian больше не имеет доступа к частным элементам класса book, компилятор C++ сообщает о синтаксических ошибках при каждой ссылке на частные данные класса book.

О друзьях класса

Обычно единственный способ, с помощью которого ваши программы могут обращаться к частным элементам класса, заключается в использовании интерфейсных функций. В зависимости от использования объектов программы иногда может быть удобным (или более эффективным с точки зрения скорости вычислений) разрешить одному классу обращаться к частным элементам другого. Для этого вы должны информировать компилятор C++, что класс является другом (friend). Компилятор, в свою очередь, позволит классу-другу обращаться к частным элементам требуемого класса. Чтобы объявить класс другом, поместите ключевое слово friend и имя класса-друга в секцию public определения класса, как показано ниже:

class abbott

{
public:
friend costello;
// Общие элементы
private:
// Частные элементы
};

Как друзья отличаются от защищенных (protected) элементов

Из урока 26 вы узнали, что в C++ существуют защищенные (protected) элементы класса, что позволяет производным классам обращаться к защищенным элементам базового класса напрямую, используя оператор точку. Помните, что к защищенным элементам класса могут обращаться только те классы, которые являются производными от данного базового класса, другими словами, классы, которые наследуют элементы базового класса (защищенные элементы класса являются как бы частными по отношению к остальным частям программы). Классы-друзья C++ обычно не связаны между собой узами наследования. Единственный способ для таких не связанных между собой классов получить доступ к частным элементам другого класса состоит в том, чтобы этот другой класс информировал компилятор, что данный класс является другом.
sageАноним 10/05/15 Вск 03:07:18 #131 №92626853 DELETED
Как вы только что узнали, если вы объявляете один класс другом другого класса, вы обеспечиваете классу-другу доступ к частным элементам данных этого другого класса. Вы также знаете и то, что чем больше доступа к частным данным класса, тем больше шансов на внесение ошибок в программу. Следовательно, если доступ к частным данным другого класса необходим только нескольким функциям класса, C++ позволяет указать, что только определенные функции дружественного класса будут иметь доступ к частным элементам. Предположим, например, что класс librarian, представленный в предыдущей программе, содержит много разных функций. Однако предположим, что только функциям change_catalog и get_catalog необходим доступ к частным элементам класса book. Внутри определения класса book мы можем ограничить доступ к частным элементам только этими двумя функциями, как показано ниже:

class book

{
public:
book(char , char , char );
void show_book(void);
friend char
librarian::get_catalog(book);
friend void librarian: :change_catalog( book , char );
private:
char title[64];
char author[ 64 ];
char catalog[64];
};

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

О функциях-друзьях

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

public:
friend class_name::function_name(parameter types);

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

Если ваша программа начинает ссылаться на один класс из другого, вы можете получить синтаксические ошибки, если порядок определения классов неверен. В данном случае определение класса book использует прототипы функций, определенные в классе librarian. Следовательно, определение класса librarian должно предшествовать определению класса book. Однако если вы проанализируете класс librarian, то обнаружите, что он ссылается на класс book:

class librarian

{
public:
void change_catalog(book , char );
char get_catalog(book);
};

Поскольку вы не можете поставить определение класса book перед определением класса librarian, C++ позволяет вам объявить класс book, тем самым сообщая компилятору, что такой класс есть, а позже определить его. Ниже показано, как это сделать:

class book; // объявление класса

Следующая программа LIMITFRI.CPP использует дружественные функции для ограничения доступа класса librarian к частным данным класса book. Обратите внимание на порядок определения классов:

#include <iostream.h>

#include <string.h>

class book;

class librarian

{
public:
void change_catalog(book
, char );
char
get_catalog(book);
};

class book

{
public:
book(char , char , char ) ;
void show_book (void);
friend char
librarian::get_catalog(book);
friend void librarian::change_catalog( book , char );
private:
char title[64];
char author[64];
char catalog[64];
};

book::book(char title, char author, char catalog)

{
strcpy(book::title, title);
strcpy(book::author, author);
strcpy(book::catalog, catalog);
}

void book::show_book(void)

{
cout << "Название: " << title << endl;
cout << "Автор: " << author << endl;
cout << "Каталог: " << catalog << endl;
}

void librarian::change_catalog(book
this_book, char new_catalog)

{
strcpy(this_book->catalog, new_catalog) ;
}

char
librarian::get_catalog(book this_book)

{
static char catalog[64];
strcpy(catalog, this_book.catalog);
return(catalog) ;
}

void main(void)

{
book programming( "Учимся программировать на C++", "Jamsa", "P101");
librarian library;
programming.show_book();
library.change_catalog(&programming, "Легкий C++ 101");
programming.show_book();
}

Как видите, программа сначала использует объявление, чтобы сообщить компилятору, что класс book будет определен позже. Поскольку объявление извещает компилятор о классе book, определение класса librarian может ссылаться на класс book, который еще не определен в программе.
sageАноним 10/05/15 Вск 03:07:40 #132 №92626865 DELETED
Что такое идентификатор класса

Идентификатор представляет собой имя, например имя переменной или класса. Если ваши программы используют дружественные классы, то может случиться, что определение одного класса ссылается на другой класс (его имя или идентификатор), о котором компилятор C++ еще ничего не знает. В таких случаях компилятор C++ будет сообщать о синтаксических ошибках. Чтобы избавиться от ошибок типа "что следует определять сначала", C++ позволяет вам включать в начало исходного текста программы объявление класса, тем самым вводя идентификатор класса:

class class_name;

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

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

В данном уроке вы изучили, как использовать классы-друзья для обращения к частным элементам другого класса напрямую с использованием оператора точки. В уроке 29 вы изучите, как использовать в C++ шаблоны функций для упрощения определения подобных функций. Но прежде чем перейти к уроку 29 убедитесь, что вы освоили следующее:

Использование в ваших программах на C++ друзей позволяет одному классу обращаться к частным элементам другого класса напрямую, используя оператор точку.
Для объявления одного класса другом (friend) другого класса вы должны внутри определения этого другого класса указать ключевое слово friend, за которым следует имя первого класса.
После объявления класса другом по отношению к другому классу, все функции-элементы класса-друга могут обращаться к частным элементам этого другого класса.
Чтобы ограничить количество дружественных методов, которые могут обращаться к частным данным класса, C++ позволяет указать дружественные функции. Для объявления функции-друга вы должны указать ключевое слово friend, за которым следует прототип функции, которой, собственно, и необходимо обращаться к частным элементам класса.
При объявлении дружественных функций вы можете получить синтаксические ошибки, если неверен порядок определений классов. Если необходимо сообщить компилятору, что идентификатор представляет имя класса, который программа определит позже, вы можете использовать оператор такого вида class class_name;.
Аноним 10/05/15 Вск 03:08:02 #133 №92626888 
ОХ ЛОЛ
Ебин тратит на сегу больше времени, чем требуется на регистрацию + расширение + фап
Ну что поделать, всякие люди бывают
sageАноним 10/05/15 Вск 03:08:04 #134 №92626891 DELETED
После того как вы создали и отладили (удалили ошибки) несколько программ, вы уже способны предвидеть ошибки, которые могут встретиться в программе. Например, если ваша программа читает информацию из файла, ей необходимо проверить, существует ли файл и может ли программа его открыть. Аналогично, если ваша программа использует оператор new для выделения памяти, ей необходимо проверить и отреагировать на возможное отсутствие памяти. По мере увеличения размера и сложности ваших программ вы обнаружите, что необходимо включить много таких проверок по всей программе. Из этого урока вы узнаете, как использовать исключительные ситуации C++ для упрощения проверки и обработки ошибок. К концу данного урока вы освоите следующие основные концепции:

Исключительная ситуация (exception) представляет собой неожиданное событие — ошибку — в программе.
В ваших программах вы определяете исключительные ситуации как классы.
Чтобы заставить ваши программы следить за исключительными ситуациями, необходимо использовать оператор C++ try.
Для обнаружения определенной исключительной ситуации ваши программы используют оператор C++ catch.
Для генерации исключительной ситуации при возникновении ошибки ваши программы используют оператор C++ throw.
Если ваша программа обнаруживает исключительную ситуацию, она вызывает специальную (характерную для данной исключительной ситуации) функцию, которая называется обработчиком исключительной ситуации.
Некоторые (старые) компиляторы не поддерживают исключительные ситуации C++.
C++ ПРЕДСТАВЛЯЕТ ИСКЛЮЧИТЕЛЬНЫЕ СИТУАЦИИ КАК КЛАССЫ

Ваша цель при использовании исключительных ситуаций C++ состоит в упрощении обнаружения и обработки ошибок в программах. В идеале, если ваши программы обнаруживают неожиданную ошибку (исключительную ситуацию), им следует разумным образом ее обработать вместо того, чтобы просто прекратить выполнение.

В программах вы определяете каждую исключительную ситуацию как класс. Например, следующие ситуации определяют три исключительные ситуации для работы с файлами:

class file_open_error {};
class file_read_error {};
class file_write_error {};

Позже в этом уроке вы создадите исключительные ситуации, которые используют переменные и функции-элементы класса. А пока просто поверьте, что каждая исключительная ситуация соответствует классу.

КАК ЗАСТАВИТЬ C++ ПРОВЕРЯТЬ ИСКЛЮЧИТЕЛЬНЫЕ СИТУАЦИИ

Прежде чем ваши программы могут обнаружить и отреагировать на исключительную ситуацию, вам следует использовать оператор C++ try для разрешения обнаружения исключительной ситуации. Например, следующий оператор try разрешает обнаружение исключительной ситуации для вызова функции file_соpy:

try

{
file_copy("SOURCE.ТХТ", "TARGET.ТХТ") ;
};

Сразу же за оператором try ваша программа должна разместить один или несколько операторов catch, чтобы определить, какая исключительная ситуация имела место (если она вообще была):

try

{
file_copy("SOURCE.ТХТ", "TARGET.ТХТ") ;
};

catch (file_open_error)

{
cerr << "Ошибка открытия исходного или целевого файла" << endl;
exit(1);
}

catch (file_read_error)

{
cerr << "Ошибка чтения исходного файла" << endl;
exit(1);
}

catch (file_write_error)

{
cerr << "Ошибка записи целевого файла" << endl;
exit(1);
}

Как видите, приведенный код проверяет возникновение исключительных ситуаций работы с файлами, определенных ранее. В данном случае независимо от типа ошибки код просто выводит сообщение и завершает программу. В идеале ваш код мог бы отреагировать и не так — возможно, попытаться исключить причину ошибки и повторить операцию. Если вызов функции прошел успешно и исключительная ситуация не выявлена, C++ просто игнорирует операторы catch.
Аноним 10/05/15 Вск 03:08:22 #135 №92626911 
>>92625699
Хуйню вангуешь, дурак.
Какие японские прокси, сайт вообще американский.
sageАноним 10/05/15 Вск 03:08:23 #136 №92626913 DELETED
ИСПОЛЬЗОВАНИЕ ОПЕРАТОРА throw ДЛЯ ГЕНЕРАЦИИ ИСКЛЮЧИТЕЛЬНОЙ СИТУАЦИИ

Сам C++ не генерирует исключительные ситуации. Их генерируют ваши программы, используя оператор C++ throw. Например, внутри функции file_copy программа может проверить условие возникновения ошибки и сгенерировать исключительную ситуацию:

void file_copy(char source, char target)

{
char line[256];
ifstream input_file(source);
ofstream output_file(target);
if (input_file.fail())
throw(file_open_error);
else
if (output_file.fail()) throw(file_open_error);
else

{
while ((! input_file.eof()) && (! input_file.fail()))

{
input_file.getline(line, sizeof(line)) ;
if (! input_file.fail()) output_file << line << endl;
else throw(file_read_error);
if (output_file.fail()) throw (file_write_error) ;
}
}
}

Как видите, программа использует оператор throw для генерации определенных исключительных ситуаций.

Как работают исключительные ситуации

Когда вы используете исключительные ситуации, ваша программа проверяет условие возникновения ошибки и, если необходимо, генерирует исключительную ситуацию, используя оператор throw. Когда C++ встречает оператор throw, он активизирует соответствующий обработчик исключительной ситуации (функцию, чьи операторы вы определили в классе исключительной ситуации). После завершения функции обработки исключительной ситуации C++ возвращает управление первому оператору, который следует за оператором try, разрешившим обнаружение исключительной ситуации. Далее, используя операторы catch, ваша программа может определить, какая именно исключительная ситуация возникла, и отреагировать соответствующим образом.

ОПРЕДЕЛЕНИЕ ОБРАБОТЧИКА ИСКЛЮЧИТЕЛЬНОЙ СИТУАЦИИ

Когда ваша программа генерирует исключительную ситуацию, C++ запускает обработчик исключительной ситуации (функцию), чьи операторы вы определили в классе исключительной ситуации. Например, следующий класс исключительной ситуации nuke_meltdown определяет операторы обработчика исключительной ситуации в функции nuke_meltdown:

class nuke_meltdown

{
public:
nuke_meltdown(void) { cerr << "\а\а\аРаботаю! Работаю! Работаю!" << endl; }
};

В данном случае, когда программа сгенерирует исключительную ситуацию nuke_meltdown, C++ запустит операторы функции nuke_meltdown, прежде чем возвратит управление первому оператору, следующему за оператором try, разрешающему обнаружение исключительной ситуации. Следующая программа MELTDOWN.CPP иллюстрирует использование функции nuke_meltdown. Эта программа использует оператор try для разрешения обнаружения исключительной ситуации. Далее программа вызывает функцию add_u232 c параметром amount. Если значение этого параметра меньше 255, функция выполняется успешно. Если же значение параметра превышает 255, функция генерирует исключительную ситуацию nuke_meltdown:

#include <iostream.h>

class nuke_meltdown

{
public:
nuke_meltdown(void) { cerr << "\а\а\аРаботаю! Работаю! Работаю!" << endl; }
};

void add_u232(int amount)

{
if (amount < 255) cout << "Параметр add_u232 в порядке" << endl;
else throw nuke_meltdown();
}

void main(void)

{
try

{
add_u232(255);
}
catch (nuke_meltdown)

{
cerr << "Программа устойчива" << endl;
}
}

Если вы откомпилируете и запустите эту программу, на экране дисплея появится следующий вывод:
sageАноним 10/05/15 Вск 03:08:40 #137 №92626928 DELETED
С:\> MELTDOWN <ENTER>

Работаю! Работаю! Работаю!

Программа устойчива

Если вы проследите исходный код, который генерирует каждое из сообщений, то сможете убедиться, что поток управления при возникновении исключительной ситуации проходит в обработчик исключительной ситуации и обратно к оператору catch. Так, первая строка вывода генерируется обработчиком исключительной ситуации, т.е. функцией nuke_meltdown. Вторая строка вывода генерируется в операторе catch, который обнаружил исключительную ситуацию.

Определение обработчика исключительной ситуации

Когда C++ обнаруживает в программе исключительную ситуацию, он запускает специальную функцию, которая называется обработчиком исключительной ситуации. Для определения обработчика исключительной ситуации вы просто создаете функцию в классе исключительной ситуации, которая имеет такое же имя, как и сама исключительная ситуация (подобно конструктору). Когда ваша программа в дальнейшем сгенерирует исключительную ситуацию, C++ автоматически вызовет соответствующий обработчик. В идеале обработчик исключительной ситуации должен выполнить операции, которые бы исправили ошибку, чтобы ваша программа могла повторить операцию, ставшую причиной ошибки. После завершения обработки исключительной ситуации выполнение вашей программы продолжается с первого оператора, следующего за оператором try, разрешившего обнаружение исключительной ситуации.

ИСПОЛЬЗОВАНИЕ ЭЛЕМЕНТОВ ДАННЫХ ИСКЛЮЧИТЕЛЬНОЙ СИТУАЦИИ

В предыдущих примерах ваши программы, используя оператор catch, могли определить, какая именно исключительная ситуация имела место, и отреагировать соответствующим образом. В идеале, чем больше информации об исключительной ситуации могут получить ваши программы, тем лучше они смогут отреагировать на ошибку. Например, в случае с исключительной ситуацией file_open_error вашей программе необходимо знать имя файла, который вызвал ошибку. Аналогично, для файловых исключительных ситуаций file_read_error или file_write_error программа, возможно, захочет узнать расположение байта, на котором произошла ошибка. Чтобы сохранить подобную информацию об исключительной ситуации, ваши программы могут просто добавить элементы данных в класс исключительной ситуации. Если в дальнейшем ваша программа сгенерирует исключительную ситуацию, она передаст эту информацию функции обработки исключительной ситуации в качестве параметра, как показано ниже:

throw file_open_error(source);
throw file_read_error(344);

В обработчике исключительной ситуации эти параметры могут быть присвоены соответствующим переменным класса (очень похоже на конструктор). Например, следующие операторы изменяют исключительную ситуацию file_open_error, чтобы присвоить имя файла, который вызвал ошибку, соответствующей переменной класса:

class file_open_error

{
public:
file_open_error(char *filename) { strcpy(file_open_error::filename, filename); }
char filename[255] ;
};
sageАноним 10/05/15 Вск 03:09:00 #138 №92626946 DELETED
ОБРАБОТКА НЕОЖИДАННЫХ ИСКЛЮЧИТЕЛЬНЫХ СИТУАЦИЙ

Из урока 11 вы узнали, что компиляторы C++ предоставляют функции библиотеки этапа выполнения, которые вы можете использовать в своих программах. Если вы читали документацию по этим функциям, то могли обратить внимание на функции, которые генерируют определенные исключительные ситуации. В таких случаях ваши программы должны проверять соответствующие исключительные ситуации. По умолчанию если ваша программа генерирует исключительную ситуацию, которая не улавливается (программа не имеет соответствующего обработчика исключительной ситуации), то запустится стандартный обработчик, предоставляемый языком C++. В большинстве случаев стандартный обработчик завершит вашу программу. Следующая программа UNCAUGHT.CPP иллюстрирует, как стандартный обработчик исключительной ситуации завершает выполнение вашей программы:

#include <iostream.h>

class some_exception { };

void main(void)

{
cout << "Перед генерацией исключительной ситуации" << endl;
throw some_exception();
cout << "Исключительная ситуация сгенерирована" << endl;
}

В данном случае, когда программа генерирует исключительную ситуацию (которая не улавливается программой), C++ вызывает стандартный обработчик исключительной ситуации, который завершает программу. Поэтому последний оператор программы, который выводит сообщение о генерации исключительной ситуации, никогда не выполняется. Вместо использования стандартного обработчика исключительной ситуации C++ ваши программы могут определить свой собственный стандартный обработчик (обработчик по умолчанию). Чтобы сообщить компилятору C++ о своем стандартном обработчике, ваши программы должны использовать функцию библиотеки этапа выполнения set_unexpected. Прототип функции set_unexpected определен в заголовочном файле except.h.

ОБЪЯВЛЕНИЕ ГЕНЕРИРУЕМЫХ ФУНКЦИЕЙ ИСКЛЮЧИТЕЛЬНЫХ СИТУАЦИЙ

Как вы уже знаете, прототип функции позволяет вам определить тип возвращаемого функцией значения и типы ее параметров. Если ваши программы используют исключительные ситуации, вы также можете использовать прототип функции, чтобы указать генерируемые данной функцией исключительные ситуации. Например, следующий прототип функции сообщает компилятору, что функция power_plant может генерировать исключительные ситуации melt_down и radiation_leak:

void power_plant(long power_needed) throw (melt_down, radiation_leak);

Включая подобным образом возможные исключительные ситуации в прототип функции, вы можете легко сообщить другому программисту, который будет читать ваш код, какие исключительные ситуации ему необходимо проверять при использовании данной функции.
sageАноним 10/05/15 Вск 03:09:17 #139 №92626959 DELETED
ИСКЛЮЧИТЕЛЬНЫЕ СИТУАЦИИ И КЛАССЫ

При создании класса вы, возможно, захотите определить исключительные ситуации, характерные для данного класса. Чтобы создать исключительную ситуацию, характерную для конкретного класса, просто включите эту исключительную ситуацию в качестве одного из общих (public) элементов класса. Например, следующее описание класса string определяет две исключительные ситуации:

class string

{
public:
string(char str);
void fill_string(
str);
void show_string(void);
int string_length(void);
class string_empty { } ;
class string_overflow {};
private:
int length;
char string[255];
};

Как видите, этот класс определяет исключительные ситуации string_empty и string_overflow. В своей программе вы можете проверить наличие исключительной ситуации, используя оператор глобального разрешения и имя класса, как показано ниже:

try

{
some_string.fill_string(some_long_string);
};
catch (string::string_overflow)

{
cerr << "Превышена длина строки, символы отброшены" << endl;
}

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Исключительные ситуации предназначены для упрощения и усовершенствования обнаружения и обработки ошибочных ситуаций в ваших программах. Для проверки и обнаружения исключительных ситуаций ваши программы должны использовать операторы try, catch и throw. Ваши знания исключительных ситуаций зависят от опыта программирования на C++. Прежде чем продолжить программировать на C++, убедитесь, что вы освоили следующие основные концепции:

Исключительная ситуация представляет собой неожиданную ошибку в вашей программе.
Ваши программы должны обнаруживать и обрабатывать (реагировать на) исключительные ситуации.
В программах вы определяете исключительную ситуацию как класс.
Вы должны использовать оператор try, чтобы заставить компилятор C++ разрешить обнаружение исключительных ситуаций.
Вы должны разместить оператор catch сразу же после оператора try, чтобы определить, какая именно исключительная ситуация имела место (если она вообще была).
C++ сам не генерирует исключительные ситуации. Ваши программы генерируют исключительные ситуации с помощью оператора throw.
Если ваша программа обнаруживает исключительную ситуацию, она вызывает специальную функцию, которая называется обработчиком исключительной ситуации.
Если ваши программы используют исключительные ситуации, вы можете указать в прототипе функции, что эта функция способна генерировать исключительную ситуацию.
При чтении документации по библиотеке этапа выполнения обращайте внимание на то, что некоторые функции способны генерировать исключительные ситуации.
Если ваша программа генерирует, но не улавливает (и не обрабатывает) исключительную ситуацию, C++ будет вызывать стандартный обработчик исключительной ситуации.
В заголовочном файле except.h определены прототипы функций, которые ваши программы могут использовать для указания своих собственных стандартных обработчиков не улавливаемых исключительных ситуаций.
Аноним 10/05/15 Вск 03:09:43 #140 №92626982 
>>92626888
Никогда не понял смысл этой хуйни с вайпом, кукла же все скрывает.
Хотя не удивлюсь, если этот борец с расширениями ее не имеет ЛОЛ
sageАноним 10/05/15 Вск 03:09:45 #141 №92626983 DELETED
Из урока 4 вы узнали, как объявлять и использовать переменные в своих программах. По мере усложнения программ вы будете выполнять арифметические операции, такие как сложение, вычитание, умножение и деление, над значениями, содержащимися в переменных. Данный урок описывает, как использовать арифметические операторы C++ для выполнения этих операций. К тому времени, когда вы закончите изучение данного урока, вы освоите следующие основные концепции:

Для выполнения математических операций используйте в своих программах арифметические операторы C++.
Чтобы гарантировать последовательность операций, C++ назначает приоритет каждому оператору.
Используя круглые скобки в арифметических выражениях, вы можете управлять порядком, в котором C++ выполняет операции.
Многие программы на C++ прибавляют или вычитают единицу, используя операции увеличения (++) или уменьшения (--).
После того как вы научитесь распознавать разные арифметические операторы C++, вы поймете, что выполнять математические операции очень легко!

ОСНОВНЫЕ МАТЕМАТИЧЕСКИЕ ОПЕРАЦИИ

Независимо от назначения большинство ваших программ на C++ будут складывать, вычитать, умножать или делить. Вы узнаете, что ваши программы могут выполнять арифметические операции с константами (например, 35) или с переменными (например, payment — total). Таблица 5.1 перечисляет основные математические операции C++:

Таблица 5. 1. Основные математические операции С++

Операция

Назначение

Пример

+

Сложение

total = cost + tax;

-

Вычитание

change = payment - total;

.

Умножение

tax = cost tax_rate;

/

Деление

average = total / count;

Следующая программа SHOWMATH.CPP использует cout для вывода реультата нескольких простых арифметических операций:

#include <iostream.h>

void main(void)

{
cout << "5 + 7 = " << 5 + 7 << endl;
cout << "12 - 7 = " << 12 - 7 << endl;
cout << "1.2345
2 = " << 1.23.45 2 << endl;
cout << "15 / 3 = " << 15 / 3 << endl;
}

Посмотрите внимательно на операторы программы. Обратите внимание, что каждое выражение сначала появляется в кавычках, которые обеспечивают вывод символов (например, 5 + 7 =) на экран. Затем программа выводит результат операции и символ новой строки. Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> SHOWMATH <ENTER>

5 + 7 = 12

12 - 7 = 5

1.2345
2 = 2.469

15 / 3 = 5

В данном случае программа выполняла арифметические операции, используя только постоянные значения. Следующая программа MATHVARS.CPP выполняет арифметические операции, используя переменные:

#include <iostream.h>

void main(void)

{
float cost =15.50; // Стоимость покупки
float sales_tax = 0.06; // Налог на продажу 6%
float amount_paid = 20.00; // Деньги покупателя
float tax, change, total; // Налог на продажу, сдача покупателю и общий счет
tax = cost * sales_tax;
total = cost + tax;
change = amount_paid - total;
cout << "Стоимость покупки: $" << cost << "\tHaлor: $" << tax << "\tОбщий счет: $" << total << endl;
cout << "Сдача покупателю: $" << change << endl;
}

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

С:\> MATHVARS <ENTER>

Стоимость покупки: $15.5 Налог: $0.93 Общий счет: $16.43

Сдача покупателю: $3.57
Аноним 10/05/15 Вск 03:10:08 #142 №92627008 
>>92623291
Хули это говно мне панду выдаёт?
sageАноним 10/05/15 Вск 03:10:10 #143 №92627010 DELETED
УВЕЛИЧЕНИЕ ЗНАЧЕНИЯ ПЕРЕМЕННОЙ НА 1

Обычной операцией, которую вы будете выполнять при программировании, является прибавление 1 к значению целой переменной. Например, предположим, что ваша программа использует переменную с именем count, чтобы сохранить данные о количестве напечатанных файлов. Каждый раз, когда программа печатает файл, 1 будет добавляться к текущему значению count. Используя оператор присваивания C++, ваша программа может увеличивать значение count, как показано ниже:

count = count + 1;

В данном случае программа сначала выбирает значение count, а затем добавляет к нему единицу. Далее программа записывает результат сложения обратно в переменную count. Следующая программа INTCOUNT.CPP использует оператор присваивания для увеличения переменной count (которая первоначально содержит значение 1000) на единицу (присваивая переменной результат 1001):

#include <iostream.h>

void main(void)

{
int count = 1000;
cout << "начальное значение count равно" << count << endl;
count = count + 1;
cout << "конечное значение count равно" << count << endl;
}

Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> INCCOUNT <ENTER>

начальное значение count равно 1000

конечное значение count равно 1001

Так как увеличение значения переменной представляет собой обычную операцию в программах, в C++ есть операция увеличения — двойной знак плюс (++). Операция увеличения обеспечивает быстрый способ прибавления единицы к значению переменной. Следующие операторы, например, увеличивают значение переменной count на 1:

count = count + 1; count++;

Следующая программа INC_OP.CPP использует операцию увеличения для наращивания значения переменной count на 1:

#include <iostream.h>

void main(void)

{
int count = 1000;
cout << "начальное значение count равно " << count << endl;
count++;
cout << "конечное значение count равно " << count << endl;
}

Эта программа работает так же, как INCCOUNT.CPP, которая использовала оператор присваивания для увеличения значения переменной. Когда C++ встречает операцию увеличения, он сначала выбирает значение переменной, добавляет к этому значению единицу, а затем записывает результат обратно в переменную.

Представление о префиксной (до) и постфиксной (после) операциях увеличения

При использовании операций увеличения ваши программы могут размещать оператор увеличения до или после переменной, как показано ниже:

++variable; variable++;

Так как первый оператор появляется до переменной, он называется префиксным оператором увеличения. Аналогично этому, второй оператор появляется после переменной и называется постфиксным оператором увеличения. Вам необходимо знать, что C++ трактует эти два оператора по-разному. Например, рассмотрим следующий оператор присваивания:

current_count = count++;

Этот оператор присваивания указывает C++ присвоить текущее значение count переменной current_count. В дополнение к этому постфиксный оператор увеличения заставляет C++ увеличить текущее значение count. Использование постфиксного оператора в этом случае делает показанный выше оператор эквивалентным следующим двум операторам:

current_count = count;

count = count + 1;

Теперь рассмотрим следующий оператор присваивания, который использует префиксный оператор увеличения:

current_count = ++count;

В этом случае оператор присваивания указывает C++ сначала увеличить значение count, а затем присвоить результат переменной current_count. Использование префиксного оператора увеличения делает показанный выше оператор эквивалентным следующим двум операторам:
Аноним 10/05/15 Вск 03:10:39 #144 №92627038 
Посмотрите, еблан не знает что терд можно просто скрыть.
Ну да ладно, это же небось самый раковый тред за всю историю двачей, не то что засмеялся-обосрался
Бамп вопреки обезумевшему школьнику.
sageАноним 10/05/15 Вск 03:11:03 #145 №92627053 DELETED
>>92627008
Оп наебать пытается.
sageАноним 10/05/15 Вск 03:11:21 #146 №92627077 DELETED
count = count + 1;

current_count = count;

Важно освоить префиксную и постфиксную операции увеличения, так, как они будут встречаться вам в большинстве программ на C++. Следующая программа PRE_POST.CPP иллюстрирует использование префиксной и постфиксной операций увеличения:

#include <iostream.h>

void main(void)

{
int small_count = 0;
int big_count = 1000;
cout << "small_count равно " << small_count << endl;
cout << "small_count++ производит " << small_count++ << endl;
cout << "конечное значение small_count равно " << sniall_count << endl;
cout << "big_count равно " << big_count << endl;
cout << "++big_count производит " << ++big_count << endl;
cout << "конечное значение big_count равно " << big_count << endl;
}

Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> PRE_POST <ENTER>

small_count равно 0

small_count++ производит 0

конечное значение small_count равно 1

big_count равно 1000

++big_count производит 1001

конечное значение big_count равно 1001

С переменной small_count программа использует постфиксную операцию увеличения. В результате программа выводит текущее значение переменной (0), а затем увеличивает его на 1. С переменной big_count программа использует префиксную операцию увеличения. В результате программа сначала увеличивает значение переменной (1000 + 1), а затем выводит результат (1001). Найдите время, чтобы отредактировать эту программу, и сначала измените постфиксную операцию на префиксную, а затем префиксную на постфиксную. Откомпилируйте и запустите программу, обращая внимание на то, как изменение операции изменяет вывод.

С++ обеспечивает также операции уменьшения

Как вы уже знаете, двойной знак плюс (++) представляет собой оператор увеличения C++. Подобным образом двойной знак минус (--) соответствует оператору уменьшения C++, который уменьшает значение переменной на 1. Как и в случае с операцией увеличения, C++ поддерживает префиксный и постфиксный операторы уменьшения. Следующая программа DECCOUNT.CPP иллюстрирует использование оператора уменьшения C++:

#include <iostream.h>

void main(void)

{
int small_count = 0;
int big_count = 1000;
cout << "small_count равно " << small_count << endl;
cout << "small_count-- производит " << small_count-- << endl;
cout << "конечное значение small_count равно " “ small_count << endl;
cout << "big_count равно " << big_count << endl;
cout << "--big_count производит " << --big_count << endl;
cout << "конечное значение big_count равно " << big_count << endl;
}

Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> DECCOUNT <ENTER”

small_count равно 0

small_count-- производит 0

конечное значение small_count равно -1

big_count равно 1000

—big_count производит 999

конечное значение big_count равно 999

Как видите, префиксный и постфиксный операторы уменьшения C++ работают так же, как и соответствующие операторы увеличения, с той лишь разницей, что они уменьшают значение переменной на 1.
Аноним 10/05/15 Вск 03:11:25 #147 №92627081 
>>92627008
Читай выше, тут нахуй целая война из-за этой херни.
sageАноним 10/05/15 Вск 03:11:42 #148 №92627104 DELETED
ДРУГИЕ ОПЕРАТОРЫ С++

В этом уроке описаны обычные арифметические операции C++, а также операции увеличения и уменьшения. В программах на C++ вы можете встретить одну или несколько операций, перечисленных в табл. 5.2:

Таблица 5.2. Операции C++, которые вы можете встретить в программах.

Операция

Функция

%

Взятие по модулю или остаток; возвращает остаток целочисленного деления

~ Дополнение; инвертирует биты значений
& Побитовое И
| Побитовое включающее ИЛИ
^ Побитовое исключающее ИЛИ
<< Сдвиг влево; сдвигает биты значения влево на указанное количество разрядов
>> Сдвиг вправо; сдвигает биты значения вправо на указанное количество разрядов
СТАРШИНСТВО ОПЕРАЦИЙ

При выполнении арифметических операций в C++ необходимо знать, что C++ выполняет операции в определенном порядке, основанном на старшинстве операций. Например, операция умножения выполняется до сложения. Чтобы лучше понять старшинство операций, рассмотрите следующие выражения:

result =5+23;

В зависимости от порядка, в котором C++ выполняет умножение и сложение, результат будет разным:

result =5+2
3;
=73;
= 21;
result =5+2
3;
=5+6;
= 11;

Чтобы избежать путаницы, C++ присваивает каждой операции приоритет, который определяет порядок выполнения операций. Так как C++ выполняет операции в определенном порядке, то и ваши программы будут проводить арифметические вычисления соответствующим образом.

Таблица 5.3 перечисляет старшинство операций C++. Операции, находящиеся в верхней части, имеют более высокий приоритет. Операции внутри каждой части имеют одинаковый приоритет. Если вы рассмотрите таблицу, то увидите, что в C++ умножение имеет более высокий приоритет, чем сложение. Вы не знакомы со многими операциями, представленными в таблице. В настоящее время не думайте об этих операциях. К концу изучения этой книги вы сможете использовать (и понять) каждую из них!

Таблица 5.3. Старшинство операций в C++.

Операция

Имя

Пример

:: Разрешение области видимости classname::classmember_name

::

Глобальное разрешение

::variable_name

.

Выбор элемента

object.member_name

->

Выбор элемента

pointer->membername

[]

Индексация

pointer[element]

()

Вызов функции

expression(parameters)

()

Построение значения

type(parameters)

sizeof

Размер объекта

sizeof expression

sizeof

Размер типа

sizeof(type)

++

Приращение после

variable++

++

Приращение до

++variable

--

Уменьшение после

variable--

--

Уменьшение до

-- variable

&

Адрес объекта

&variable



Разыменование

pointer

new

Создание (размещение)

new type

delete

Уничтожение (освобождение) delete pointer

delete[]

Уничтожение массива

delete pointer

~

Дополнение

~expression

!

Логическое НЕ

! expression

+

Унарный плюс

+1

-

Унарный минус

-1

()

Приведение

(type) expression

.

Выбор элемента

object.
pointer

->

Выбор элемента

object->pointer



Умножение

expression * expression

/

Деление

expression / expression

%

Взятие по модулю

expression % expression

+

Сложение (плюс)

expression + expression

-

Вычитание (минус)

expression expression
Аноним 10/05/15 Вск 03:11:50 #149 №92627111 
>>92627053
Вот пидор. Хотя я, вроде, дрочил на том сайте, и там всё заебись было, без панд.
sageАноним 10/05/15 Вск 03:12:05 #150 №92627120 DELETED
Управление порядком, в котором C++ выполняет операции

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

cost = price_a + price_b 1.06;

К сожалению, в этом случае C++ сначала выполнит умножение (price_b
1.06), а затем прибавит значение price_a.

Если ваши программы должны выполнять арифметические операции в определенном порядке, вы можете заключить выражение в круглые скобки. Когда C++ оценивает выражение, он сначала всегда выполняет операции, сгруппированные в круглых скобках. Например, рассмотрим следующее выражение:

result =(2+3) (3+4);

C++ вычисляет данное выражение в следующем порядке:

result = (2 + 3)
(3 + 4);
= (5) (3 + 4);
= 5
(7);
=57;
= 35;

Подобным образом группируя выражения внутри круглых скобок, вы можете управлять порядком, в котором C++ выполняет арифметические операции. Аналогично предыдущему примеру, ваша программа может сложить две стоимости внутри круглых скобок, как показано ниже:

cost = (price_a + price_b)
1.06;

СЛЕДИТЕ ЗА ОШИБКАМИ ПЕРЕПОЛНЕНИЯ ПРИ АРИФМЕТИЧЕСКИХ ОПЕРАЦИЯХ

Из урока 4 вы узнали, что, если вы присваиваете переменной значение, которое не входит в диапазон значений для данного типа переменной, возникает ошибка переполнения. При выполнении арифметических операций необходимо помнить о возможности возникновения ошибок переполнения. Например, следующая программа MATHOVER.CPP умножает 200 на 300 и присваивает результат переменной типа int. Однако, поскольку результат умножения (60000) превышает наибольшее возможное значение для типа int (32767), возникает ошибка переполнения:

#include <iostream.h>

void main(void)

{
int result;
result = 200 300;
cout << "200
300 = " << result << endl;
}

Когда вы откомпилируете и запустите эту программу, на экране появится следующий вывод:

С:\> MATHOVER <ENTER>

200 * 300 = -5536
sageАноним 10/05/15 Вск 03:12:23 #151 №92627137 DELETED
ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

В данном уроке вы изучали обычные арифметические операции и операции приращения C++. Чтобы программы корректно выполняли арифметические вычисления, C++ назначает каждой операции приоритет, который управляет порядком выполнения операций. Из урока 6 вы узнаете, как использовать входной поток с именем cin для выполнения операций ввода с клавиатуры. До изучения урока 6 убедитесь, что вы освоили следующее:

C++ использует операторы +,- , * и / для сложения, вычитания, умножения и деления.
C++ обеспечивает префиксные (до) и постфиксные (после) операции увеличения, которые прибавляют единицу к значению переменной.
C++ обеспечивает префиксную (до) и постфиксную (после) операции уменьшения, которые вычитают единицу из значения переменной.
Префиксные (до) операции указывают C++ сначала увеличить (или уменьшить) значение переменной, а затем использовать это значение.
Постфиксные (после) операции указывают C++ сначала использовать значение переменной, а затем увеличить (или уменьшить) его.
Чтобы гарантировать, что выражения выполняются корректно, C++ назначает каждой операции приоритет, управляющий порядком выполнения операций.
Если вам нужно управлять порядком выполнения арифметических операций, используйте круглые скобки. C++ всегда вычисляет сначала выражение в скобках.
sageАноним 10/05/15 Вск 03:12:49 #152 №92627166 DELETED
По мере увеличения размера и сложности ваших программ вам следует разделить их на небольшие легко управляемые части, называемые функциями. Каждая функция в вашей программе должна выполнять определенную задачу. Например, если вы пишете программу платежей, можете создать одну функцию, определяющую количество часов, отработанных служащим, вторую функцию, определяющую сверхурочную оплату, третью функцию, выводящую на печать и т. д. Если программе необходимо выполнить определенную задачу, то она вызывает соответствующую функцию, обеспечивая эту функцию информацией, которая ей понадобится в процессе обработки. Из этого урока вы узнаете, как создавать и использовать функции в ваших программах на C++. К концу данного урока вы освоите следующие основные концепции:

• Функции группируют связанные операторы для выполнения определенной задачи.

• Ваша программа вызывает функцию, обращаясь к ее имени, за которым следуют круглые скобки, например bеер ().

• После завершения обработки большинство функций возвращают значение определенного типа, например int или float, которое программа может проверить или присвоить переменной.

• Ваши программы передают параметры (информацию) функциям, например имя, возраст или оклад служащего, заключая параметры в круглые скобки, которые следуют за именем функции.

• C++ использует прототипы функций для определения типа возвращаемого функцией значения, а также количества и типов параметров, передаваемых функции.

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

СОЗДАНИЕ И ИСПОЛЬЗОВАНИЕ ВАШИХ ПЕРВЫХ ФУНКЦИЙ

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

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

Таблица 9. Примеры смысловых имен функций.

Имя функции

Назначение функции

Print_test_scores

Печатать тестовые очки класса

Accounts_payable

Обработать счета компании

Get_user_name

Запрос имени пользователя

Print_document

Напечатать указанный документ

Calculate_income_tax

Определить подоходный налог пользователя

Функция C++ по структуре подобна программе main, которую вы использовали во всех предыдущих программах. Другими словами, имя функции предваряется ее типом, а за ним следует список параметров, описание которых появляется в скобках. Вы группируете операторы функций внутри левой и правой фигурных скобок, как показано ниже:

тип_возврата имя_функции (список_параметров)

{

объявления_переменных;
операторы;
}
Рассмотрите, например, как структура этой функции соответствует следующей программе main:

void main (void) //-----------> тип имя (список_параметров)

{
int count; // ---------------------------> объявления_переменных;
for (count = 0; count < 10; count++) cout << count << ' '; // -----> Оператор
}

Следующие операторы определяют функцию с именем show_message, которая выводит сообщение на экран, используя cout.

void show_message (void)

{
cout << "Привет, учусь программировать на C++" << endl;
}
Аноним 10/05/15 Вск 03:13:02 #153 №92627176 
14312167821840.jpg
>>92627038
Я не понимаю, как у таких людей голова работает.

Я не смог нормально открыть ссылку с оп-поста -> плак-плак-плак я должен вайпать((

Что у него насрано вместо мозгов, что там такие охуительные реакции происходят.

Развлек на ночь глядя, какие только долбоебы на этом ресурсы не водятся, ей богу.
sageАноним 10/05/15 Вск 03:13:14 #154 №92627190 DELETED
Как вы, возможно, помните из урока 2, слово void, предшествующее имени функции, указывает функции не возвращать значение. Подобно этому, слово void, содержащееся внутри круглых скобок, указывает (компилятору C++ и программистам, читающим ваш код), что функция не использует параметры (информацию, которую программа передает функции). Следующая программа SHOW_ MSG.CPP использует функцию show_message для вывода сообщения на экран:

#include <iostream.h>

void show_message (void)

{
cout << "Привет, учусь программировать на C++" << endl;
}

void main (void)

{
cout << "Перед вызовом функции" << endl;
show_message ();
cout << "Вернулись из функции" << endl;
}

Вы уже знаете, что выполнение программы всегда начинается внутри main. Внутри main следующий оператор (вызов функции) вызывает функцию show_message:

show_message ();

Круглые скобки после имени функции сообщают компилятору C++, что ваша программа использует функцию. Позже вы узнаете, что внутри этих скобок программа может передавать в функции информацию (параметры). Если вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С: \>SHOW_MSG <ENTER>

Перед вызовом функции

Привет, учусь программировать на C++

Вернулись из функции

Если программа встречает вызов функции, она начинает выполнять операторы, находящиеся внутри функции. После того как программа выполнит все операторы, которые содержит функция (другими словами, функция завершится); выполнение программы продолжается с оператора, следующего непосредственно за вызовом функции:

#include <iostream.h>

void show_message (void)

{
cout << "Привет, учусь программировать на C++" << endl;
}

void main (void)

{
cout << "Перед вызовом функции" << endl;
show_message ();
cout << "Вернулись из функции" << endl;
}

В данном случае программа выполняет первый оператор в main, которой выводит сообщение для пользователя, что программа собирается вызвать функцию. Далее программа встречает вызов функции и запускает выполнение операторов в show_messsage. После того как программа выполнит единственный оператор функции, она возвращается обратно в main и продолжает свое выполнение с оператора, непосредственно следующего за вызовом функции. В этом случае программа выводит сообщение, извещая пользователя о том, что она возвратилась из функции, и после этого заканчивает свою работу. Следующая программа TWO__MSGS.CPP использует две функции - show_title и show_lesson для вывода информации об этой книге:
Аноним 10/05/15 Вск 03:13:30 #155 №92627207 
>>92627053
Блядь, благодаря тебе я вспомнил слово "необучаемый"
Можешь разнообразия ради сказать какой у тебя браузер? А я тебе уебану дам инструкцию в три шага за 2 минуты.
sageАноним 10/05/15 Вск 03:13:46 #156 №92627220 DELETED
#include <iostream.h>

void show_title (void)

{
cout << "Книга: Учимся программировать на C++" << endl;
}

void show_lesson (void)
{
cout << "Урок: Знакомство с функциями" << endl;
}

void main (void)
{
show_title ();
show_lesson ();
}

Когда программа начинает выполнение, она сначала вызывает функцию show_title, которая выводит сообщение с помощью cout. После завершения show_title программа вызывает функцию show_lesson, также выводящую сообщение. После завершения show_lesson программа завершается, поскольку в main больше нет операторов.

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

Вызов функции

Функция Представляет собой набор связанных операторов, которые Выполняют определенную задачу. Создавая функции внутри программы, вы можете делить большие задачи на небольшие легко управляемые части.
Ваши программы выполняют операторы функций посредством вызова функции. Для вызова функции программы просто обращаются к имени функции, за которым следуют круглые скобки, как показано ниже:

function_name();

Если программа передает информацию (параметры) в функцию, она размещает эту информацию внутри круглых скобок, разделяя ее запятыми:

payroll(employee_name, employee_id, salary);

После того как последний оператор функции завершен, выполнение программы продолжается с первого оператора следующего за вызовом функции.
Аноним 10/05/15 Вск 03:14:01 #157 №92627234 
>>92627207
Фаерфокс. Что делать, чтобы открылось? НАхуй туда вообще поставили панду?
sageАноним 10/05/15 Вск 03:14:10 #158 №92627242 DELETED
ПРОГРАММА МОЖЕТ ПЕРЕДАВАТЬ ИНФОРМАЦИЮ В ФУНКЦИИ

Для увеличения потенциальных возможностей ваших функций C++ позволяет программам передавать информацию (параметры) в функции. Если функция использует параметры, вы должны сообщить C++ тип каждого параметра, например int, float, char и т.д. Следующая функция show_number использует параметр типа int.

void show_number (int value)

{
cout << "Значение параметра равно " << value << endl;
}

Если ваша программа вызывает функцию show_number, она должна передать ей значение, как показано ниже:

show_number ( 1001 ) ; //----------------> Значение, передаваемое в функцию

C++ будет подставлять переданное число вместо каждого имени параметра value внутри функции:

show_number (1001)

void show_number (int value )

{
cout << "Значение параметра равно " << value << endl;
}

void show_number (1001)

{
cout << "Значение параметра равно " << 1001 << endl;
}

Как видите, поскольку C++ замещает значение параметра, функция show_number выводит число 1001, переданное ей главной программой.

Следующая программа USEPARAM.CPP использует функцию show_number несколько раз, каждый раз передавая разные числа:

#include <iostream.h>

void show_number (int value)

{
cout << "Значение параметра равно " << value << endl;
}

void main (void)

{
show_number (1);
show_number (1001);
show_number (-532);
}

Если вы откомпилируете и запустите эту программу, на вашем экране будет отображено следующее:

С: \>USEPARAM <ENTER>

Значение параметра равно 1

Значение параметра равно 1001

Значение параметра равно -532

Как видите, каждый раз, когда программа вызывает функцию, C++ присваивает передаваемое число переменной value. Найдите время для эксперимента с этой программой, изменяя значения, которые main передает в функцию, и обращая внимание на результат.

Каждый параметр функции имеет определенный тип. В случае функции show_number параметр value должен быть типа int. Если вы попытаетесь передать в функцию значение другого типа, например с плавающей точкой, компилятор будет сообщать об ошибке. В большинстве случаев ваши программы будут передавать несколько значений в функцию. Для каждого передаваемого параметра функция должна указать имя и тип. Например, следующая программа BIGSMALL.CPP использует функцию show_big_and_little для вывода самого большого и самого маленького из трех полученных целочисленных значений:
Аноним 10/05/15 Вск 03:14:31 #159 №92627258 
>>92627207
Забей, этот долбоеб невменяемый, это еще хуже чем необучаемый.
sageАноним 10/05/15 Вск 03:14:48 #160 №92627272 DELETED
#include <iostream.h>

void show_big_and_little (int a, int b, int c)

{
int small = a;
int big = a;

if (b > big)
big = b;

if (b < small)
small = b;

if (c > big)
big = c;

if (c < small)
small = c;

cout << "Самое большое значение равно " << big << endl;
cout << "Самое маленькое значение равно " << small << endl;
}

void main (void)

{
show_big_and_little (1, 2, 3);
show_big_and_little (500, 0, -500);
show_big_and_little (1001, 1001, 1001);
}

Если вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С: \> BIGSMALL <ENTER>

Самое большое значение равно 3

Самое маленькое значение равно 1

Самое большое значение равно 500

Самое маленькое значение равно -500

Самое большое значение равно 1001

Самое маленькое значение равно 1001

Наконец, следующая программа SHOW_EMP.CPP использует функцию show_employee для вывода возраста (тип int) и оклада (тип float) служащего:

#Include <iostream.h>

void show_employee (int age, float salary)

{
cout << "Возраст служащего " << age << " года (лет)" << endl;
cout << "Служащий получает $" << salary << endl;
}

void main (void)

{
show_employee (32, 25000.00);
}

Как видите, функция show_employee определяет параметры типа int и float

Передача параметров в функцию

Если ваша функция использует параметры, она должна указать уникальное имя и тип для каждого параметра. Когда программа вызывает функцию, C++ присваивает значения параметров именам параметров функции слева направо. Каждый параметр функции имеет определенный тип, например int, float или char. Значения, которые ваша программа передает в функцию, используя параметры, должны соответствовать типу параметров.
sageАноним 10/05/15 Вск 03:15:09 #161 №92627291 DELETED
ФУНКЦИИ МОГУТ ВОЗВРАЩАТЬ РЕЗУЛЬТАТ ВЫЗВАВШЕЙ ФУНКЦИИ

Функция должна выполнять определенную задачу для вашей программы. В большинстве случаев функции будут выполнять некоторые типы вычислений. Затем функция возвратит свой результат вызвавшей функции. Когда функция возвращает значение, вы должны сообщить C++ тип значения, например int, float, char т. д. Чтобы информировать C++ о типе возвращаемого функцией значения, просто поставьте перед именем функции соответствующий тип. Например, следующая функция add_values складывает два своих целочисленных параметра и возвращает результат типа int вызвавшей программе:

int add_values (int а, int b)

{
int result;
result= а+b;
return (result);
}

В данном случае слово int, появляющееся перед именем функции, указывает тип возвращаемого значения функции. Функции используют оператор return для возврата значения вызвавшей функции. Когда ваша программа встречает оператор return, она возвращает заданное значение и завершает выполнение функции, возвращая управление вызвавшей программе. В программе вы можете использовать возвращаемое значение, как показано ниже:

result=add_values (1, 2);

В данном случае программа присваивает возвращаемое функцией значение переменной result. Ваша программа может также сразу же напечатать возвращаемое функцией значение с помощью cout, как показано ниже:

cout << "Сумма значений равна " << add_values (500, 501) << endl;

Предыдущая реализация функции add_values использовала три оператора, чтобы было легче понять смысл функции. Однако вы можете сократить функцию до единственного оператора return, как показано ниже:

int add_values (int a, int b)

{
return (a+ b);
}

Следующая программа ADDVALUE.CPP использует функцию add_values для сложения нескольких значений:

#include <iostream.h>

int add_values (int a, int b)

{
return (a+ b);
}

void main (void)

{
cout << " 100 + 200 = " << add_values(100, 200) << endl;
cout << " 500 + 501 = " << add_values(500, 501) << endl ;
cout << "-1 + 1 = " << add_values(-1, 1) << endl;
}

Выберите время для эксперимента с этой программой, изменяя значения, которые программа передает в функцию. Вы могли бы попытаться передать в функцию большие значения, например 20000 и 30000. Как и можно предложить, выполнение функции, возвращающей значение типа int, приведет к ошибке переполнения.

Не все функции возвращают значение типа int. Следующая функция average_ value возвращает среднее двух целочисленных значений, которое может быть дробным, например 3.5:

float average_value(int a, int b)

{
return( (a + b) / 2.0);
}

В этом случае слово float, которое предшествует имени функции, указывает тип возвращаемого функцией значения.
Аноним 10/05/15 Вск 03:15:09 #162 №92627292 
>>92627234
https://addons.mozilla.org/ru/firefox/addon/exhentai-easy-2/
Ставешь на лису эту шарагу, регаешься на e-hentai.org
ВСЕ БЛЯДЬ.
sageАноним 10/05/15 Вск 03:15:25 #163 №92627312 DELETED
ФУНКЦИИ, КОТОРЫЕ НЕ ВОЗВРАЩАЮТ ЗНАЧЕНИЕ

Если функция не возвращает значение, вам необходимо предварить имя функции типом void. В противном случае вы должны предварять имя функции типом возвращаемого функцией значения, например, int, float, char и т. д. Чтобы возвратить значение вызвавшей функции, функция использует оператор return. Когда ваша программа встречает оператор return, выполнение функции завершается и указанное значение возвращается вызвавшей функции. Возможны ситуации, когда вы встретите оператор return в функции, которая не возвращает значение:

return;

В этом случае функция имеет тип void (не возвращает значение) и оператор return просто завершает выполнение функции.

Замечание: Если операторы появляются в функции после оператора return они не будут выполняться. Как уже обсуждалось выше, если ваша программа встречает оператор return в функции, то возвращается соответствующее значение, функция заканчивается и выполнение программы продолжается с первого оператора, следующего за вызовом функции.

ИСПОЛЬЗОВАНИЕ ВОЗВРАЩАЕМОГО ФУНКЦИЕЙ ЗНАЧЕНИЯ

Когда функция возвращает значение, вызвавшая программа может присвоить возвращенное значение переменной, используя оператор присваивания, как показано ниже:

payroll_amount = payroll (employee, hours, salary);

В дополнение к этому вызвавшая программа просто может обращаться к функции. Например, следующий оператор выводит возвращаемое функцией значение, используя cout.

cout << "Служащий получил" << payroll(employee, hours, salary) < < endl;

Вызвавшая функция может также использовать возвращаемое значение в условии, как показано ниже:

if (payroll(employee, hours, salary) < 500.00)
cout << "Этот служащий нуждается в повышении" << endl;

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

ПРЕДСТАВЛЕНИЕ О ПРОТОТИПАХ ФУНКЦИЙ

Прежде чем ваша программа сможет вызвать функцию, C++ должен знать тип воозвращаемого значения, а также количество и тип параметров, используемых функцией. В каждой из программ, представленных в этом уроке, определение функции, вызываемой программой, всегда предшествует вызову функции в исходном файле. Однако в большинстве случаев функции появляются в вашем исходном файле и, как правило, одна функция вызывает другую. Чтобы гарантировать, что C++ знает особенности каждой функции, используемой в программе, вы можете поместить прототипы функций в начало исходного файла. В общем случае прототип функции обеспечивает информацию о типе возвращаемого функцией значения и ее параметрах. Следующий оператор иллюстрирует прототипы функций для нескольких функций, используемых в данном уроке:

void show_message(void);

void show_number(int);

void show_employee(int, float);

int add_values(int, int);

float average_value(int, int);

Как видите, прототип функции указывает тип возвращаемого значения, а также количество и тип каждого параметра. Обратите внимание на точку с запятой в конце каждого прототипа.
Аноним 10/05/15 Вск 03:15:46 #164 №92627328 DELETED
>>92627292
Это вирус, не ставьте пацаны.
Аноним 10/05/15 Вск 03:15:54 #165 №92627333 
>>92627292
Отлично. А нахуй так сложно? На другие сайты я спокойно захожу.
sageАноним 10/05/15 Вск 03:16:03 #166 №92627343 DELETED
float average_value ( int, int) ;

Если ваша программа вызывает функцию, для которой компилятор C++ не нашел определения или прототипа, компилятор сообщает о синтаксической ошибке. При исследовании заголовочных файлов C++ или других программ вы будете постоянно сталкиваться с прототипами функций. Следующая программа PROTO.CPP иллюстрирует использование прототипа Функции:

#include <iostream.h>

float average_value(int, int); // Прототип функции

void main(void)

{
cout << "Среднее значение 2000 и 2 равно " << average_value(2000, 2) << endl;
}

float average_value (int a, int b)

{
return((a + b) / 2.0);
}

В этом случае программа вызывает функцию average_value до того, как функция определена. Таким образом, программа использует прототип функции, который предваряет определение main. Если вы удалите прототип функции и откомпилируете эту программу, компилятор C++ будет сообщать о синтаксических ошибках.

Использование прототипов функций

Прототип функции сообщает компилятору C++ тип возвращаемого значения, а также количество и тип параметров функции. Когда вы компилируете вашу программу, компилятор C++ использует прототип каждой функции, чтобы убедиться, что вы не перепутали тип возвращаемого функцией значения (например, не присваиваете возвращаемое значение типа float переменной типа int) и что вы не передаете в качестве параметра значение неверного типа. Раньше многие компиляторы C++ не выполняли подобную проверку. В результате программисты часто тратили часы, пытаясь найти ошибки, возникающие из-за того, что вместо ожидаемого значения типа float в функцию передавалось значение типа int. Если вы встречаете синтаксическую ошибку, которая возникает из-за противоречия с прототипом функции, будьте благодарны. В прошлом компилятор не определял подобную ошибку, и ваша программа просто не работала.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Из этого урока вы узнали, как использовать функции в программах на C++. Данный урок охватил целый ряд основных понятий, таких как параметры, типы возвращаемых значений и прототипы функций. Сейчас вы можете потратить несколько больше времени на эксперименты с простыми программами. Из урока 10 вы узнаете, как изменять значения параметров внутри функций. Однако до изучения урока 10 убедитесь, что вы освоили следующие основные концепции:

По мере усложнения ваших программ вам следует делить их на небольшие легко управляемые части, называемые функциями. Каждая функция должна иметь уникальное имя. Присваивайте вашим функциям имена, выражающие смысл задач, которые выполняют функции.
Функции могут возвращать значение вызвавшей функции. При этом вы должны указать тип возвращаемого функцией значения (int, char и т. д.) до имени функции, в противном случае вы должны предварять имя функции словом void.
Программы передают информацию в функции с помощью параметров. Если функция получает параметры, вы должны указать уникальное имя и тип каждого параметра. Если функция не получает параметры, вы должны поместить ключевое слово void внутри круглых скобок, следующих за именем функции.
C++ должен знать тип возвращаемого значения и количество и тип параметров, которые получает функция. Если определение функции следует за использованием функции, вы должны поместить прототип функции в начале исходного файла.
Аноним 10/05/15 Вск 03:16:27 #167 №92627365 DELETED
>>92627333
Оп наебщик просто, троянов пропихивает, куда модеры смотрят.
Аноним 10/05/15 Вск 03:16:45 #168 №92627384 
Ручной вайп есть ничто иное как ода к батхерту, его наивысшее выражение.
К нему прибегают только тогда, когда рвется так, что уже выпадают кишки.
sageАноним 10/05/15 Вск 03:16:53 #169 №92627392 DELETED
При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. В прошлом (если вы программировали на языке С), когда у вас была функция с именем add_values, которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Например, вы могли бы использовать add_two_values и add_three_values. Аналогично если вы хотели использовать подобную функцию для сложения значений типа float, то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой. В этом уроке вы научитесь использовать перегруженные функции. К концу данного урока вы освоите следующие основные концепции:

• Перегрузка функций позволяет вам использовать одно и то же имя для нескольких функций с разными типами параметров.

• Для перегрузки функций просто определите две функции с одним и тем же именем и типом возвращаемого значения, которые отличаются количеством параметров или их типом.

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

ПЕРВОЕ ЗНАКОМСТВО С ПЕРЕГРУЗКОЙ ФУНКЦИЙ

Перегрузка функций позволяет вашим программам определять несколько функций с одним и тем же именем и типом возвращаемого значения. Например, следующая программа перегружает функцию с именем add_values. Первое определение функции складывает два значения типа int. Второе определение функции складывает три значения. В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать:

#include <iostream.h>

int add_values(int a,int b)

{
return(a + b);
)

int add_values (int a, int b, int c)

(
return(a + b + c);
)

void main(void)

{
cout << "200 + 801 = " << add_values(200, 801) << endl;
cout << "100 + 201 + 700 = " << add_values(100, 201, 700) << endl;
}

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

Подобным образом следующая программа MSG_OVR.CPP перегружает функцию show_message. Первая функция с именем show_message выводит стандартное сообщение, параметры ей не передаются. Вторая выводит передаваемое ей сообщение, а третья выводит два сообщения:

#include <iostream.h>

void show_message(void)

{
cout << "Стандартное сообщение: " << "Учимся программировать на C++" << endl;
}

void show_message(char message)

{
cout << message << endl;
}

void show_message(char
first, char *second)

{
cout << first << endl;
cout << second << endl;
}

void main(void)

{
show_message();
show_message("Учимся программировать на языке C++!");
show_message("B C++ нет предрассудков!","Перегрузка - это круто!") ;
}
sageАноним 10/05/15 Вск 03:17:13 #170 №92627412 DELETED
КОГДА НЕОБХОДИМА ПЕРЕГРУЗКА

Одним из наиболее общих случаев использования перегрузки является применение функции для получения определенного результата, исходя из различных параметров. Например, предположим, что в вашей программе есть функция с именем day_of_week, которая возвращает текущий день недели (0 для воскресенья, 1 для понедельника, ..., 6 для субботы). Ваша программа могла бы перегрузить эту функцию таким образом, чтобы она верно возвращала день недели, если ей передан юлианский день в качестве параметра, или если ей переданы день, месяц и год:

int day_of_week(int julian_day)

{
// Операторы
}

int day_of_week(int month, int day, int year)

{
// Операторы
}

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

Перегрузка функций улучшает удобочитаемость программ

Перегрузка функций C++ позволяет вашим программам определять несколько функций с одним и тем же именем. Перегруженные функции должны возвращать значения одинакового типа, но могут отличаться количеством и типом параметров. До появления перегрузки функций в C++ программисты языка С должны были создавать несколько функций с почти одинаковыми именами. К сожалению программисты, желающие использовать такие функции, должны были помнить, какая комбинация параметров соответствует какой функции. С другой стороны, перегрузка функций упрощает задачу программистов, требуя, чтобы они помнили только одно имя функции.
Перегруженные функции не обязаны возвращать значения одинакового типа по той причине, что компилятор однозначно идентифицирует функцию по ее имени и набору ее аргументов. Для компилятора функции с одинаковыми именами, но различными типами аргументов — разные функции, поэтому тип возвращаемого значения — прерогатива каждой функции. — Прим.перев.
Аноним 10/05/15 Вск 03:17:26 #171 №92627423 
>>92627333
Хуй знает, там какая-то ебала с рекламодателями и все такое, я вообще понятия не имею.
Ну и чтобы аутисты возгорались
sageАноним 10/05/15 Вск 03:17:39 #172 №92627433 DELETED
Из урока 10 вы узнали, как изменять параметры внутри функции с помощью указателей. Для использования указателей вы должны предварять имена переменных-указателей звездочкой. Использование указателей досталось в "наследство" от языка С. Чтобы упростить процесс изменения параметров, С++ вводит такое понятие как ссылка. Как вы узнаете из этого урока, ссылка представляет собой псевдоним (или второе имя), который ваши программы могут использовать для обращения к переменной. К концу данного урока вы освоите следующие основные концепции:

• Для объявления и инициализации ссылки внутри программы объявите переменную, размещая амперсанд (&) сразу же после типа переменной, и затем используйте оператор присваивания для назначения псевдонима, например int& alias_name = variable',.

• Ваши программы могут передавать ссылки в функцию в качестве параметров, а функция, в свою очередь, может изменять соответствующее значение параметра, не используя указателей.

• Внутри функции вам следует объявить параметр как ссылку, размещая амперсанд (&) после типа параметра, затем можно изменять значение параметра внутри функции без помощи указателей.

Как вы узнаете, использование указателей очень упрощает изменение значений параметров внутри функции.

ССЫЛКА ЯВЛЯЕТСЯ ПСЕВДОНИМОМ

Ссылка C++ позволяет создать псевдоним (или второе имя) для переменных в вашей программе. Для объявления ссылки внутри программы укажите знак амперсанда (&) непосредственно после типа параметра. Объявляя ссылку, вы должны сразу же присвоить ей переменную, для которой эта ссылка будет псевдонимом, как показано ниже:

int& alias_name = variable; //---> Объявление ссылки

После объявления ссылки ваша программа может использовать или переменную , или ссылку:

alias_name = 1001;
variable = 1001;

Следующая программа SHOW_REF.CPP создает ссылку с именем alias_name и присваивает псевдониму переменную number. Далее программа использует как ссылку, так и переменную:

#include <iostream.h>

void main(void)

{
int number = 501;
int& alias_name = number; // Создать ссылку
cout << "Переменная number содержит " << number << endl;
cout << "Псевдоним для number содержит " << alias_name << endl;
alias_name = alias_name + 500;
cout << "Переменная number содержит " << number << endl;
cout << "Псевдоним для number содержит " << alias_name << endl;
}

Как видите, программа прибавляет 500 к ссылке alias_name. В итоге программа прибавляет 500 также и к соответствующей переменной number, для которой ссылка служит псевдонимом или вторым именем. Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> SHOW_REF <ENTER>

Переменная number содержит 501

Псевдоним для number содержит 501

Переменная number содержит 1001

Псевдоним для number содержит 1001

В общем случае использование ссылки таким образом, как только что было показано, создает трудности для понимания. Однако вы увидите, что использование ссылок значительно упрощает процесс изменения параметров внутри функции.

Объявление ссылки

Ссылка C++ представляет собой псевдоним (второе имя), которое ваши программы могут использовать для обращения к переменной. Для объявления ссылки поставьте амперсанд (&) сразу же после типа переменной, а затем укажите имя ссылки, за которым следует знак равенства и имя переменной, для которой ссылка является псевдонимом:
float& salary_alias = salary;
Аноним 10/05/15 Вск 03:17:40 #173 №92627435 
Завтра специально пересоздам тред.
sageАноним 10/05/15 Вск 03:18:03 #174 №92627456 DELETED
СПОЛЬЗОВАНИЕ ССЫЛОК В КАЧЕСТВЕ ПАРАМЕТРОВ

Основное назначение ссылки заключается в упрощении процесса изменения параметров внутри функции. Следующая программа REFERENC.CPP присваивает ссылку с именем number_alias переменной number. Программа передает ссылку на переменную в функцию change_value, которая присваивает переменной значение 1001:

#include <iostream.h>

void change_value(int &alias)

{
alias = 1001;
}

void main(void)

{
int number;
int& number_alias = number;
change_value(number_alias);
out << "Переменная number содержит " << number << endl;
}

Как вы видите, программа передает ссылку в функцию change_value. Если вы рассмотрите объявление функции, вы обнаружите, что change_value объявляет параметр alias как ссылку на значение типа int.

void change_value(int& alias)

Внутри функции change_value можете изменять значение параметра без помощи указателя. В результате звездочка () не используется и операция внутри функции становится легче для понимания.

Использование комментариев для объяснения ссылок внутри ваших программ

Большинство программистов C++ знакомы с языком программирования С, и они привыкли использовать указатели внутри функции, если необходимо изменить значение параметра. В результате, если такие программисты не видят указатели внутри функций, которые используют ссылки, они могут предположить, что значения параметров не изменяются. Для предотвращения подобных промахов не забывайте размещать несколько комментариев до и внутри функций, которые изменяют параметры с помощью ссылок. В таком случае программисты С лучше поймут работу ваших функций.
Рассмотрим второй пример

В уроке 10 вы использовали следующую функцию для перестановки двух значений с плавающей точкой:

void swap_values(float
a, float b)

{
float temp;
temp =
a;
a = b;
*b = temp;
}

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

#include <iostream.h>

void swap_values(float& a, float& b)
{ float temp;
temp = a;
a = b;
b = temp;
}

void main(void)

{ float big = 10000.0;
float small = 0.00001;
float& big_alias = big;
float& small_alias = small;
swap_values(big_alias, small_alias);
cout << "Big содержит " << big << endl;
cout << "Small содержит " << small << endl;
}

Как видите, функцию swap_values сейчас легче понять, однако ваша программа имеет теперь два дополнительных имени (ссылки big_alias и small_alias), за которыми вы должны следить.
sageАноним 10/05/15 Вск 03:18:22 #175 №92627469 DELETED
ПРАВИЛА РАБОТЫ СО ССЫЛКАМИ

Ссылка не является переменной. Один раз присвоив значение ссылке, вы уже не можете ее изменить. Кроме того в отличие от указателей вы не можете выполнить следующие операции над ссылками:

• Вы не можете получить адрес ссылки, используя оператор адреса C++.

• Вы не можете присвоить ссылке указатель.

• Вы не можете сравнить значения ссылок, используя операторы сравнения C++.

• Вы не можете выполнить арифметические операции над ссылкой, например добавить смещение.

•Вы не можете изменить ссылку.

По мере использования объектно-ориентированного программирования на C++ вы вернетесь к ссылкам.

Использование ссылок для изменения параметров функции

Из урока 10 вы узнали, что ваши программы с помощью указателей могут изменять значение параметров внутри функции. Для изменения параметра вы должны передать его адрес в функцию. Чтобы получить адрес параметра, используйте оператор адреса C++ (&). В свою очередь функция использует переменные-указатели (которые хранят адрес памяти). Для объявления переменной-указателя внутри функции предваряйте имя параметра звездочкой (). Чтобы изменить или использовать значение параметра внутри функции, предваряйте каждое обращение к имени этого параметра оператором разыменования C++ (). К сожалению, многие операции внутри функции комбинируют переменные-указатели и переменные-неуказатели.
Ссылки C++ упрощают процесс изменения параметров функции, избавляя от операторов, которые смешивают переменные-указатели и переменные-неуказатели.

ЧТ0 ВАМ НЕОБХОДИМО ЗНАТЬ

Из этого урока вы узнали, как использовать ссылки C++ для создания псевдонима или второго имени переменной. Использование ссылок может упростить функции, изменяющие значения параметров. Из урока 15 вы узнаете, что C++ позволяет вам задавать значения по умолчанию для параметров функции. При вызове функции программа может опускать значения одного или нескольких параметров и функция будет использовать значения по умолчанию. До изучения урока 15 убедитесь, что вы освоили следующие основные концепции:

Ссылка C++ является псевдонимом (или вторым именем) переменной.
Для объявления ссылки поместите знак амперсанда (&) непосредственно после типа переменной, а затем укажите имя ссылки, за которым следует знак равенства и имя переменной, для которой ссылка является псевдонимом.
Если вы однажды присвоили ссылке значение, вы не можете его изменить.
Вам следует помещать несколько комментариев до и внутри функций, которые используют ссылки для изменения значений параметра, чтобы другие программисты, читающие ваш код, сразу обратили, на это внимание.
Чрезмерное использование ссылок может привести к слишком трудному для понимания программному коду.
Аноним 10/05/15 Вск 03:18:42 #176 №92627487 
>>92627365
Без антивируса там кстати и впправду лучше поменьше лазит
Каспер например раз в три года че-то несет, мол какая-то ссылка заблокирована
Бесполезный антивирус
sageАноним 10/05/15 Вск 03:18:47 #177 №92627490 DELETED
Как вы уже знаете, тип переменной определяет набор значений, которые она может хранить, а также набор операций, которые можно выполнять над этой переменной. Например, над значением переменной типа int ваша программа может выполнять сложение, вычитание, умножение и деление. С другой стороны, использование оператора плюс для сложения двух строк лишено всякого смысла. Когда вы определяете в своей программе класс, то по существу вы определяете новый тип. А если так, C++ позволяет вам определить операции, соответствующие этому новому типу.

Перегрузка оператора состоит в изменении смысла оператора (например, оператора плюс (+), который обычно в C++ используется для сложения) при использовании его с определенным классом. В данном уроке вы определите класс string и перегрузите операторы плюс и минус. Для объектов типа string оператор плюс будет добавлять указанные символы к текущему содержимому строки. Подобным образом оператор минус будет удалять каждое вхождение указанного символа из строки. К концу данного урока вы изучите следующие основные концепции:

Вы перегружаете операторы для улучшения удобочитаемости ваших программ, но перегружать операторы следует только в том случае, если это упрощает понимание вашей программы.
Для перегрузки операторов программы используют ключевое слово C++ operator.
Переопределяя оператор, вы указываете функцию, которую C++ вызывает каждый раз, когда класс использует перегруженный оператор. Эта функция, в свою очередь, выполняет соответствующую операцию.
Если ваша программа перегружает оператор для определенного класса, то смысл этого оператора изменяется только для указанного класса, оставшаяся часть программы будет продолжать использовать этот оператор для выполнения его стандартных операций.
C++ позволяет перегружать большинство операторов, за исключением четырех, перечисленных в таблице 24, которые программы не могут перегружать.
Перегрузка операторов может упростить наиболее общие операции класса и улучшить читаемость программы. Найдите время для эксперимента с программами, представленными в этом уроке, и вы обнаружите, что перегрузка операторов выполняется очень просто.

ПЕРЕГРУЗКА ОПЕРАТОРОВ ПЛЮС И МИНУС

Когда вы перегружаете оператор для какого-либо класса, то смысл данного оператора не изменяется для переменных других типов. Например, если вы перегружаете оператор плюс для класса string, то смысл этого оператора не изменяется, если необходимо сложить два числа. Когда компилятор С++ встречает в программе оператор, то на основании типа переменной он определяет ту операцию, которая должна быть выполнена.

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

class string

{
public:
string(char ); // Конструктор
void str_append(char
);
void chr_minus(char);
void show_string(void);
private:
char data[256] ;
};

Как видите, класс определяет функцию str_append, которая добавляет указанные символы к содержимому строки класса. Аналогичным образом функция chr_minus - удаляет каждое вхождение указанного символа из строки класса. Следующая программа STRCLASS.CPP использует класс string для создания двух объектов символьных строк и манипулирования ими.

#include <iostream.h>

#include <string.h>

class string

{
public:
string(char ); // Конструктор
void str_append(char
);
void chr_minus(char);
void show_string(void);
private:
char data[256] ;
};

string::string(char str)

{
strcpy(data, str);
}

void string::str_append(char
str)

{
strcat(data, str);
}

void string::chr_minus(char letter)

{
char temp[256] ;
int i, j;
for (i = 0, j = 0; data; i++) // Эту букву необходимо удалить?
if (data != letter) // Если нет, присвоить ее temp
temp[j++] = data;
temp[j] = NULL; // Конец temp
// Копировать содержимое temp обратно в data
strcpy(data, temp);
}

void string::show_string(void)

{
cout << data << endl;
}

void main(void)

{
string title( "Учимся программировать на языке C++");
string lesson("Перегрузка операторов");
title.show_string() ;
title.str_append(" я учусь!");
itle.show_string();
lesson.show_string();
lesson.chr_minus('p') ;
lesson.show_string();
}
sageАноним 10/05/15 Вск 03:19:04 #178 №92627511 DELETED
Как видите, программа использует функцию str_append для добавления символов к строковой переменной title. Программа также использует функцию chr_minus для удаления каждой буквы " р" из символьной строки lesson. В данном случае программа использует вызовы функции для выполнения этих операций. Однако, используя перегрузку операторов, программа может выполнять идентичные операции с помощью операторов плюс (+) и минус (-).

При перегрузке оператора используйте ключевое слово C++ operator вместе с прототипом и определением функции, чтобы сообщить компилятору C++, что класс будет использовать этот метод как оператор. Например, следующее определение класса использует ключевое слово operator, чтобы назначить операторы плюс и минус функциям str_append и chr_minus внутри класса string:

class string

{
public:
string(char ); // Конструктор
void operator +(char
);
void operator -(char); ————— Определение операторов класса void show_string(void);
private:
char data[256];
};

Как видите, класс перегружает операторы плюс и минус. Как уже упоминалось, когда класс перегружает оператор, он должен указать функцию, которая реализует операцию, соответствующую этому оператору. В случае оператора плюс определение такой функции становится следующим:

void string::operator +(char str)

{
strcat(data, str);
}

Как видите, определение этой функции не содержит имени, поскольку здесь определяется перегруженный оператор класса. Для перегрузки оператора плюс программа не изменила обработку, которая осуществляется внутри функции (код этой функции идентичен коду предыдущей функции str_append). Вместо этого программа просто заменила имя функции ключевым словом operators соответствующим оператором. Следующая программа OPOVERLD.CPP иллюстрирует использование перегружаемых операторов плюс и минус:

#include <iostream.h>

#include <string.h>

class string

{
public:
string(char
); // Конструктор
void operator +(char );
void operator -(char);
void show_string(void);
private;
char data[256] ;
};

string::string(char
str)

{
strcpy(data, str);
}

void string::operator +(char *str)

{
strcat(data, str);
}

void string::operator -(char letter)

{
char temp[256] ;
int i, j;
for (i = 0, j = 0; data; i++) if (data[il 1= letter) temp[j++] = data;
temp[j] = NULL;
strcpy(data, temp);
}

void string::show_string(void)

{
cout << data << endl;
}

void main(void)

{
string title( "Учимся программировать на C++");
string lesson("Перегрузка операторов");
title.show_string();
title + " я учусь!";
title.show_string() ;
lesson.show_string();
lesson - 'P';
lesson.show_string();
}

Как видите, программа использует перегруженные операторы:

title + " я учусь!"; // Добавить текст " я учусь!"

lesson - 'р'; // Удалить букву 'р'
sageАноним 10/05/15 Вск 03:19:21 #179 №92627528 DELETED
В данном случае синтаксис оператора законен, но немного непривычен. Обычно вы используете оператор плюс в выражении, которое возвращает результат, например, как в операторе some_str = title + "текст ";. Когда вы определяете оператор, C++ предоставляет вам полную свободу в отношении поведения оператора. Однако, как вы помните, ваша цель при перегрузке операторов состоит в том, чтобы упростить понимание ваших программ. Поэтому следующая программа STR_OVER.CPP немного изменяет предыдущую программу, чтобы позволить ей выполнять операции над переменными типа string, используя синтаксис, который более согласуется со стандартными операторами присваивания:

#include <iostream.h>

#include <string.h>

class string

{
public:
string(char ); // Конструктор
char
operator +(char ) ;
char
operator -(char);
void show_string(void);
private:
char data[256] ;
} ;

string::string(char str)

{
strcpy(data, str);
}

char
string::operator +(char str)

{
return(strcat(data, str));
}

char
string::operator -(char letter)

{
char temp[256];
int i, j;
for (i = 0, j = 0; data; i++) if (data 1= letter) temp[j++] = data;
temp[j] = NULL;
return(strcpy(data, temp));
}

void string::show_string(void)

{
cout << data << endl;
}

void main(void)

{
string title("Учимся программировать на C++");
string lesson("Перегрузка операторов");
title.show_string();
title = title + " я учусь";
title.show_string() ;
lesson.show_string();
lesson = lesson - '?';
lesson.show_string();
}

Изменив перегруженные операторы плюс и минус таким образом, чтобы они возвращали указатель на символьную строку, программа может теперь использовать эти операторы в привычном для оператора присваивания виде:

title = title + " учимся программировать!";

lesson = lesson - 'р';

Второй пример

При создании ваших собственных типов данных с помощью классов наиболее общей операцией будет проверка, являются ли два объекта одинаковыми. Используя перегрузку, ваши программы могут перегрузить операторы равенства (==), неравенства (!=) или другие операторы сравнения. Следующая программа COMP_STR.CPP добавляет новый оператор в класс string, который проверяет, равны ли два объекта string. Используя перегрузку операторов, ваши программы могут проверять, содержат ли строковые объекты одинаковые строки, как показано ниже:

if (some_string == another_string)

Ниже приведена реализация программы COMP_STR.CPP:

#include <iostream.h>

#include <string.h>

class string

{
public:
string(char ); // конструктор
char
operator +(char );
char
operator -(char);
int operator ==(string);
void show_string(void);
private:
char data[256];
};

string::string(char str)

{
strcpy(data, str);
}

char
string::operator +(char str)

{
return(strcat(data, str));
}

char
string::operator -(char letter)

{
char temp[256];
int i, j;
for (i = 0, j = 0; data; i++) if (data 1= letter) temp[j++] = data;
temp[j] = NULL;
return(strcpy(data, temp));
}

int string::operator ==(string str)

{
int i;
for (i = 0; data == str.data; i++)
if ((data == NULL) && (str.data == NULL)) return(1); // Равно
return (0); //He равно
}

void string::show_string(void)

{
cout << data << endl;
}

void main(void)

{
string title( "Учимся программировать на C++");
string lesson("Перегрузка операторов");
string str( "Учимся программировать на C++");
if (title == lesson) cout << "title и lesson равны" << endl;
if (str == lesson) cout << "str и lesson равны" << endl;
if (title == str) cout << "title и str равны" << endl;
}

Как видите, перегружая операторы подобным образом, вы упрощаете понимание ваших программ.
Аноним 10/05/15 Вск 03:19:40 #180 №92627539 
>>92627423
Ну хуй их поймёт, они там какие-то тупые.
sageАноним 10/05/15 Вск 03:19:42 #181 №92627541 DELETED
ОПЕРАТОРЫ, КОТОРЫЕ Вbl HE МОЖЕТЕ ПЕРЕГРУЗИТЬ

В общем случае ваши программы могут перегрузить почти все операторы С++. В табл. 24 перечислены операторы, которые C++ не позволяет перегружать.

Таблица 24. Операторы C++, которые ваши программы не могут перегрузить.

Оператор



Назначение

Пример

. Выбор элемента object.member
. Указатель на элемент object.member
:: Разрешение области видимости classname::member
?:

Условный оператор сравнения

с = (а > b) ? а : b;
ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Перегрузка операторов — это возможность назначать новый смысл операторам при использовании их с определенным классом. Используя перегрузку операторов, вы можете повысить удобочитаемость ваших программ и облегчить их понимание, выражая операции класса более понятным образом. Из урока 25 вы узнаете, как разделить данные между объектами с помощью элемента static и как использовать методы класса, когда никакие объекты класса не объявляются. До изучения урока 25 убедитесь, что вы освоили следующее:

Чтобы перегрузить оператор, вы должны определить класс, которому оператор будет назначен.
Когда вы перегружаете оператор, перегрузка действует только для класса, в котором он определяется. Если программа использует оператор с неклассовыми переменными (например, переменными типа int или float), используется стандартное определение оператора.
Чтобы перегрузить оператор класса, используйте ключевое слово C++ operator для определения метода класса, который C++ вызывает каждый раз, когда переменная класса использует оператор.
C++ не позволяет вашим программам перегружать оператор выбора элемента (.), оператор указателя на элемент (.*), оператор разрешения области видимости (::) и условный оператор сравнения (?:).
Аноним 10/05/15 Вск 03:20:03 #182 №92627564 
Эксхентай хорош тем, что там дохуя всего есть, вот и все.
sageАноним 10/05/15 Вск 03:20:33 #183 №92627592 DELETED
ASCII ( от англ. American Standard Code for Information Interchange) — американский стандартный код для обмена информацией. ASCII представлена в виде таблицы печатных символов и некоторых специальных управляющих символов, каждому символу соответствует уникальный код в диапазоне от [0;255]. ASCII представляет собой кодировку для представления десятичных цифр, латиницы и кириллицы, знаков препинания и управляющих символов (см. Таблица 1).

Таблица 1 — Таблица символов ASCII
№ 0 1 2 3 4 5 6 7 8 9
0 nul sox stx etx eot enq ack bel bs ht
1 nl vt ff cr so si dle dc1 dc2 dc3
2 dc4 nak syn etb can em sub esc fs gs
3 rs us space ! « # $ % & ‘
4 ( ) + , - . / 0 1
5 2 3 4 5 6 7 8 9 : ;
6 < = > ? @ A B C D E
7 F G H I J K L M N O
8 P Q R S T U V W X Y
9 Z [ \ ] ^ _ ‘ a b c
10 d e f g h i j k l m
11 n o p q r s t u v w
12 x y z { | } ~ del А Б
13 В Г Д Е Ж З И Й К Л
14 М Н О П Р С Т У Ф Х
15 Ц Ч Ш Щ ъ Ы Ь Э Ю Я
16 а б в г д е ж з и й
17 к л м н о п
18
19
20
21
22 р с т у ф х
23 ц ч ш щ ъ ы ь э ю я
24 Ё ё Є є Ї ї Ў ў ° ·
25 · № ¤
Таблица 1 представляет собой полный набор символов ASCII. В таблице ASCII, символы с нулевого по 31 включительно, являются управляющими ASCII символами. Это значит, что данные символы выполняют некоторые действия, причём эти символы печатаются с сочетанием клавиши Ctrl. В таблице 1 записано обозначение управляющих ASCII символов, но не их отображение при печати. Остальные же символы с 32 по 254 не являются управляющими и имеют вид как и в таблице 1.

Благодаря таблице ASCII появилась новая форма представления изображений, с помощью символов таблицы ASCII.

ASCII art — форма изобразительного искусства, использующая символы ASCII для представления изображений. При создании такого изображения используются символы букв, цифр и знаков пунктуации. В ASCII art используется около 95 символов таблицы ASCII. Так как национальные представления таблиц ASCII различаются, поэтому остальные 160 символов не используются в искусстве ASCII.

1
2
3
4
5
6
7
8
9
10
// искусство ASCII (ASCII art)надпись сгенерирована программой
/
__ __
/\ \__ /\ \ __
___ _____ _____ ____\ \ ,_\ __ __ \_\ \ /\_\ ___ ___ ___ ___ ___
/'___\/\ '__`\ /\ '__`\ /',__\\ \ \/ /\ \/\ \ /'_` \\/\ \ / __`\ /'___\ / __`\ /' __` __`\
/\ \__/\ \ \L\ \\ \ \L\ \/\__, `\\ \ \_\ \ \_\ \/\ \L\ \\ \ \ /\ \L\ \ __ /\ \__/ /\ \L\ \/\ \/\ \/\ \
\ \____\\ \ ,__/ \ \ ,__/\/\____/ \ \__\\ \____/\ \___,_\\ \_\\ \____//\_\\ \____\\ \____/\ \_\ \_\ \_\
\/____/ \ \ \/ \ \ \/ \/___/ \/__/ \/___/ \/__,_ / \/_/ \/___/ \/_/ \/____/ \/___/ \/_/\/_/\/_/
\ \_\ \ \_\
\/_/ \/_/ */
Изначально ASCII art выполнялось вручную, и это была рутинная работа. Сейчас существует огромное количество программ, так называемых, генераторов ASCII art. Такие программы автоматически создают ASCII изображения.
sageАноним 10/05/15 Вск 03:20:59 #184 №92627609 DELETED
В некоторых источниках говорится, что оператор выбора if else — самостоятельный оператор. Но это не так, if else — это всего лишь форма записи оператора выбора if. Оператор if else позволяет определить программисту действие, когда условие истинно и альтернативное действие, когда условие ложно. Тогда как if позволял определить действие при истинном условии.

Синтаксис записи оператора выбора if else:

1
2
3
4
5
6
7
if (/проверяемое условие/)
{
/тело оператора выбора 1/;
} else
{
/тело оператора выбора 2/;
}
Читается так: «Если проверяемое условие истинно, то выполняется тело оператора выбора 1, иначе (то есть проверяемое условие ложно) выполняется тело оператора выбора 2«. Обратите внимание на то, как записан оператор if else. Слово else специально сдвинуто вправо для того чтобы программный код был понятен и его было удобно читать.

Рассмотрим задачу с предыдущей темы, с использованием if else. Напомню условие задачи: «Даны два числа, необходимо их сравнить».

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// if_else.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
int a, b;
cout << "Vvedite pervoe chislo: ";
cin >> a;
cout << "Vvedite vtoroe chislo: ";
cin >> b;
if ( a >= b) // если a больше либо равно b, то
{
cout << a << " >= " << b << endl;
} else // иначе
{
cout << a << " <= " << b << endl;
}
system("pause");
return 0;
}
В данном коде нас интересуют строки 14-20. Эти строки читаются так: если a (первое число) больше либо равно b (второе число), то выполнить оператор вывода в строке 16

1
cout << a << " >= " << b << endl;
иначе выполнить оператор вывода в строке 19

1
cout << a << " <= " << b << endl;
В данном ифе мы используем операции соотношений >= и <=. Условие перехода не совсем правильно, так как условие будет ложно только в том случае, если первое число будет меньше второго, во всех остальных случаях условие истинно. Значит, строку 19 нужно записать так

1
cout << a << " < " << b << endl; // в кавычках записать не меньше или равно, а просто меньше.
А вот так сработала программа (см. Рисунок 1).

CppStudio.com
Vvedite pervoe chislo: 15
Vvedite vtoroe chislo: -4
15 >= -4
Для продолжения нажмите любую клавишу . . .
Рисунок 1 — Оператор выбора if else

Покажу еще один пример использования операторов выбора if else (так называемые вложенные операторы if else для множественного выбора).
Аноним 10/05/15 Вск 03:21:13 #185 №92627618 
>>92627564
Да не может ведь такого быть!
Ведь вайподебил считает, что exhentai это на уровне фингербокса.
sageАноним 10/05/15 Вск 03:21:18 #186 №92627620 DELETED
Условие задачи:
Составить алгоритм находящий значение y, если у=х, при х<0; у=0, при 0<=х<30; у=х2, при х>=30;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// inif_else.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char argv[])
{
int x, y;
cout << "Vvedite x: ";
cin >> x;
if (x < 0)
{
y = x; // выполняется, если х меньше нуля
} else
{
if ( (x >= 0) && (x < 30) )
{
y = 0; // выполняется, если х больше либо равно нуля и меньше 30
} else
{
if (x >= 30)
{
y = x
x; // выполняется, если х больше либо равен 30
}
}
}
cout << "y=" << y << endl;
system("pause");
return 0;
}
В данной задаче возможны три случая:
1-й случай: х < 0;
2-й случай: х лежит в пределах от 0 (включая 0) до 30;
3-й случай: х больше или равен 30.

Заметьте новшество!! В 17 строке такую запись: if ( ( x >= 0 ) && ( x< 30 ) ), я использовал символы && — это логическое И. Операция логического И && необходима для объединения нескольких простых условий в одно составное. В нашем случае необходимо проверить истинность двух условий: первое – x >= 0, второе – х < 30. Все проверяемое условие будет истинно, если истинны два простых условия. В математике правильной записью считается такая запись: 0 <= x < 30, а в С++ правильной записью считается вот такая запись: ( x >= 0 ) && ( x < 30 ) или такая 0 <= x && x < 30. Кстати круглые скобочки () && () не обязательны, так как условия простые, но для уверенности, я прописываю, всегда, данные скобочки и вам советую.

Разбор частного случая:

Допустим, пользователь ввел число 31. Начиная со строки 12, выполняется проверка условий. Читается так: «Если х (31 в нашем случае) < 0, то выполнить оператор в строке 14». Но так как 31 > 0 условие ложно мы переходим к слову else (иначе) строка 15. Дальше проверяем, входит ли число 31 в заданный интервал. Читается так: если х>=0 и х<30 то выполнить оператор в строке 19. Но так как число 31 не входит в заданный интервал, то условие ложно. Подробно строка 17: программа сначала проверит первое простое условие х >= 0 – оно истинно, а если первое истинно, то программа перейдет к проверке второго простого условия х < 30 – оно ложно. Следовательно всё составное условие ложно, ведь в составном условии у нас используется логическая операция &&, а это значит, что все составное условие истинно только в том случае, когда истинны оба простых условия. Переходим к else (иначе ), здесь у нас последний if, (строка 22). Выполняется проверка х >= 30. Читается так: Если х >= 30 то выполнить оператор, находящийся в строке 24. Наконец-то условие истинно, итак выполнился оператор в строке 24. И строка 28 печатает получившееся значение. Ну, все, рассмотрели программу по мельчайшим деталям. Результат работы программы, в случае, если пользователь ввел число 31 (см. Рисунок 2)

CppStudio.com
Vvedite x: 31
y=961
Для продолжения нажмите любую клавишу . . .
Рисунок 2 — Оператор выбора if else
sageАноним 10/05/15 Вск 03:21:44 #187 №92627640 DELETED
Итак мы рассмотрели оператор с одиночным выбором if и оператор с двойным выбором if else, но в С++ еще имеется оператор множественного выбора switch, который мы сейчас детально рассмотрим.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// форма записи оператора множественного выбора switch
switch (/переменная или выражение/)
{
case /константное выражение1/:
{
/группа операторов/;
break;
}
case /константное выражение2/:
{
/группа операторов/;
break;
}
//. . .
default:
{
/группа операторов/;
}
}
На начальном этапе анализируется выражение или переменная. После чего осуществляется переход к той ветви программы, для которой значение переменной или выражения совпадает с указанным константным выражением. Далее выполняется оператор или группа операторов пока не встретиться зарезервированное слово break или закрывающая фигурная скобочка. Если значение переменной или выражения не совпадает ни с одним константным выражением, то передается управление ветви программы содержащей зарезервированное слово default. После чего выполняется оператор или группа операторов данной ветви. Сейчас рассмотрим задачу с использованием оператора выбора switch.

Условие задачи: написать программу, которая складывает, вычитает, умножает, делит два числа введенных с клавиатуры. Разработать пользовательский интерфейс.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// switch.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char argv[])
{
int count; // переменная для выбора в switch
double a,b; // переменные для хранения операндов
cout << "Vvedite pervoe chislo: ";
cin >> a;
cout << "Vvedite vtoroe chislo: ";
cin >> b;
cout << "Vibirite deistvie: 1-clojenie; 2-vichitanie; 3-ymnojenie; 4-delenie: ";
cin >> count;
switch (count) // начало оператора switch
{
case 1: // если count = 1
{
cout << a << " + " << b << " = " << a + b << endl; // выполнить сложение
break;
}
case 2: // если count = 2
{
cout << a << " - " << b << " = " << a - b << endl; // выполнить вычитание
break;
}
case 3: // если count = 3
{
cout << a << "
" << b << " = " << a * b << endl; // выполнить умножение
break;
}
case 4: // если count = 4
{
cout << a << " / " << b << " = " << a / b << endl; // выполнить деление
break;
}
default: // если count равно любому другому значению
cout << "Nepravilni vvod" << endl;
}
system("pause");
return 0;
}
В 9-й строке мы объявили переменную count целочисленного типа. Именно значение данной переменной программа будет сравнивать со значением константного выражения. В строке 10 объявлены две переменные вещественного типа данных, для хранения введённых чисел. Почему вещественного, объясню позже. С 17 по 41 строки записан условный оператор множественного выбора switch. На начальном этапе анализируется переменная count. Анализируется таким образом:
если переменная count равна единице, значит, выполняется блок операторов с 20-й по 23-ю строки;
если переменная count равна двойке, значит, выполняется блок операторов с 25-й по 28-ю строки;
если переменная count равна тройке, значит, выполняется блок операторов с 30-й по 33-ю строки;
если переменная count равна четырем, значит, выполняется блок операторов с 35-й по 38-ю строки;
sageАноним 10/05/15 Вск 03:22:05 #188 №92627654 DELETED
Если же значение переменной count не совпадает ни с одним константным выражением, то передается управление ветви программы содержащей зарезервированное слово default. То есть будет выполнена следующая строка

1
cout << "Nepravilni vvod" << endl;
Оператор switch может содержать, а может и не содержать зарезервированное слово default. Если значение переменной не совпадет ни с одним константным выражением и не будет default, то программное управление в этом случае просто перешло бы к первому оператору после switch. В строках 19, 24, 29, 34 записаны константные выражения, с которыми программа сравнивает значение переменной count.
В строках 22, 27, 32, 37, записан оператор break. Возникает вопрос: «Зачем он нужен?» Допустим, пользователь ввел 2, то есть переменная count инициализировалась двойкой. Начинает работать условный оператор множественного выбора switch. То есть выполняется поиск двойки в константных выражениях. Сначала проверяется строка 19, мы видим, что в строке 19 константное выражение равно единице, а нам нужна двойка. Проверяем дальше. А дальше по порядку строка 24. Мы видим, что в строке 24 константное выражение равно двойке, то, что нужно!!! Переменная count равна константному выражению, выполняется блок операторов с 25 по 28 строки. И вот в 27-й строке записан оператор break, который заставляет программу перейти к первому оператору после оператора switch. В данном случае управление передается строке 42. А нужен этот переход только для того, чтобы не выполнялись заведомо ненужные действия. Если убрать оператор break, то программа будет дальше сравнивать значение переменной с константными выражениями, пока они все не закончатся и потом все равно передаст управление строке 42. Результат работы программы показан ниже (см. Рисунок 1).

Оператор множественного выбора switch C++

Рисунок 1 — Оператор множественного выбора в С++

Вернемся к сроке 10, там объявляются две переменные типа double. Наверное, возникает вопрос, «Почему вещественного типа, а не целочисленного?». Отвечаю: «Потому, что одно из действий, которые может выполнять программа является деление, а при делении результат имеет вещественный тип данных. Компилятор С++ при делении чисел обращает внимание на их типы данных. Если мы просто делим числа на калькуляторе, например 4/5=0.8 Компилятор С++ нам выдаст результат при таком делении 0. Так как оба числа являются целочисленными, значит, результат тоже будет целочисленный, то есть целая часть от обычного деления, а в нашем случае целая часть при таком делении – это 0, соответственно часть информации теряется, или как еще говорят, отсекается (восемь десятых, в нашем случае, отсекается). А если делимое и делитель поменять местами, на калькуляторе получим: 5/4=1.25; компилятор С++ покажет несколько иной результат, а именно 5/4=1 (0.25 отсекается). Такое явление в С++ называется неявным приведением типа.» Вещественный тип данных используется для более точного представления чисел, чем целочисленный (то есть отображает дробную часть).

В С++ существуют два вещественных типа данных:
1) double – вещественный тип данных двойной точности, а значит занимает вдвое больше памяти, чем тип float
2) float – вещественный тип данных одинарной точности
Практика
Аноним 10/05/15 Вск 03:22:29 #189 №92627678 
Тут кстати какой-то долбоеб про форчан говорил.
Так там же ексхентай треды постоянно висят.
sageАноним 10/05/15 Вск 03:22:33 #190 №92627680 DELETED
Оператор continue используется только в циклах. В операторах for, while, do while, оператор continue выполняет пропуск оставшейся части кода тела цикла и переходит к следующей итерации цикла. Рассмотрим фрагмент кода с оператором continue.

1
2
3
4
5
6
7
8
// пример использования оператора continue:
int count = 0;
do // начало цикла do while
{
continue;
count++;
}
while ( count < 10 )
Посмотрите внимательно на выше приведенный пример, и Вы увидите, что do while бесконечный, так как каждая итерация цикла приводит к выполнению оператора continue, который пропускает операцию инкремента переменной-счётчика count и переходит на следующую итерацию цикла. Таким образом значение в переменной count не меняется, а значит и условие всегда будет истинным. Разработаем программу с оператором continue. Программа должна работать циклически. Внутри цикла необходимо организовать ввод чисел.Если введено число от 0 до 10 включительно, то необходимо напечатать квадрат этого числа, иначе используя оператор continue пропустить оператор возведения в квадрат введенного числа. При введении отрицательного числа осуществить выход из цикла.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// continue.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char argv[])
{
int in_number; // переменная для хранения введённого числа
do
{
cout << "Enter number: ";
cin >> in_number; // считываем введённое число в переменную in_number
if ( in_number > 10 || in_number < 0 ) // если введённое число не входит в заданный интервал
continue; // переход на следующую итерацию цикла do while
cout << "square = " << in_number
in_number << endl; // возводим в квадрат введённое число
} while ( in_number >= 0 ); // пока введённое число больше либо равно нулю цикл будет работать
system("pause");
return 0;
}
Цикличность в программе организуем циклом с постусловием - do while. В цикле сначала считываем введённое число в переменную in_number, после чего, выполняется проверка условия в операторе if. Условие оператора условного выбора if будет истинным в том случае, если введённое число будет строго меньше нуля или строго больше 10. Заданный интервал — [0;10], число взятое из этого интервала возводится в квадрат. Истинность условия оператора if приводит к выполнению оператора continue в строке 15. А оператор continue пропускает операторы в строке 16 и переходит к проверке условия продолжения цикла do while. Условие в цикле будет истинно, пока вводимые числа будут строго больше 0. Результат работы программы показан на рисунке 3.

CppStudio.com
Enter number: 15
Enter number: 11
Enter number: 5
square = 25
Enter number: 9
square = 81
Enter number: -23
Для продолжения нажмите любую клавишу . . .
Рисунок 3 — Оператор continue в С++

Сначала вводились числа 15 и 11, эти числа не входят в заданный интервал и поэтому квадрат этих чисел не вычислялся. Числа 5 и 9 принадлежат заданному интервалу, а значит программа должна вычислить их квадраты. Когда ввели отрицательное число do while завершил свою работу. Теперь переделаем эту программу так, чтобы можно было обойтись без оператора continue, то есть напишем эквивалент оператору continue.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// continue.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char argv[])
{
int in_number; // переменная для хранения введённого числа
do
{
cout << "Enter number: ";
cin >> in_number; // считываем введённое число в переменную in_number
if ( in_number <= 10 && in_number >= 0 ) // если введённое число входит в заданный интервал
//continue; // переход на следующую итерацию цикла do while
cout << "square = " << in_number
in_number << endl; // возводим в квадрат введённое число
} while ( in_number >= 0 ); // пока введённое число больше либо равно нулю цикл будет работать
system("pause");
return 0;
}
В эквивалентной программе без оператора continue код уменьшился на одну строку — закомментированная строка 15. Изменилось условие оператора условного выбора if. Строка 16 - это тело оператора if, а значит, если условие в if истинно будет выполнятся оператор в строке 16.

Многие программисты считают, что операторы break и continue нарушают структурность программного кода и предпочитают обходится без них. Так, что если есть возможность обойтись без операторов break и continue, лучше так и сделать. Тем более, что операторы break и continue всегда можно заменить структурированными операторами, мы это доказали, показав эквивалентные программы.

Практика
sageАноним 10/05/15 Вск 03:22:57 #191 №92627694 DELETED
Обращаюсь к новичкам, которые только начали изучать указатели: «Если вас заинтересовала эта тема и вы хотите в ней разобраться, что я могу вам сказать — ситуация не из приятных!» ))) Кто бы и как бы усердно и старательно не объяснял вам что к чему, понять указатели на указатели сложно. Сам указатель на указатель содержит в себе адрес, который ссылается на другой адрес, а он, в свою очередь, ссылается на адрес в памяти, где хранятся данные. Вроде бы и можно понять. Но как это применять на практике? Зачем оно надо??? Это понять сложнее. А надо «оно», среди прочего, для возможности работы с массивами указателей, которые указывают на память с данными (строками, например). Каждый элемент этого массива — это указатель, который содержит в себе адрес строки (первого элемента символьного массива):

Снимок

Наша ситуация усложняется еще и тем, что в данной статье мы постараемся доступно показать, как выделять динамическую память под двумерный массив указателей и как ее освобождать. Ну что, испугались? Тогда начнем разбираться! Если вы еще слабо знаете тему Указатели, прочтите все таки сначала эту статью. Она поможет вам подготовиться к восприятию темы Указатель на указатель.

А в данной статье мы будем рассматривать пример, в котором перед нами ставится следующая задача: у нас есть указатель на указатель char pp (он будет содержать адрес массива указателей на строки) и размер этого массива int size, который изначально равен 0. Нам надо написать функцию, которая будет выделять динамическую память для новых элементов массива указателей и для хранения символов новых строк. Эта функция будет принимать, как параметры, указатель на указатель, размер массива указателей и строку, которую надо будет записать в выделенную под нее память. Чтобы не усложнять задачу, в ней не будет диалога с пользователем. Пять строк мы определим сразу при вызовах функции.

Если у вас есть возможность, пишите исходный код по мере прочтения. Так будет легче его понять. Детальные объяснения увидите под кодом.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>;
#include <string.h>;

using namespace std;

char
AddPtr (char pp, int size, char str); //прототип функции

int main()
{
setlocale(LC_ALL, "rus");

int size = 0;//количество указателей на строки
char
pp = 0;//указатель на массив указателей, которые содержат адреса строк

cout << "~~~~~Добавляем указатели на пять строк и заполняем строки данными~~~~~" << endl;
//вызов функции и присваивание возвращаемого значения
pp = AddPtr(pp, size, "11111111111111111");
size++; //=1 увеличиваем размер массива указателей

pp = AddPtr(pp, size, "22222222222222222");
size++; //2

pp = AddPtr(pp, size, "33333333333333333");
size++; //3

pp = AddPtr(pp, size, "44444444444444444");
size++; //4

pp = AddPtr(pp, size, "55555555555555555");
size++; //5

for(int i = 0; i < size; i++) //показываем все строки на экран
cout << pp << endl; //достаточно обратиться к pp - это адрес строки (0-й элемент)
cout << endl;

for(int i = 0; i < size; i++) //освобождаем память
{
delete [] pp; // сначала выделенную под строки
}
delete [] pp; // потом выделенную под массив указателей
return 0;
}

char AddPtr (char pp, int size, char str)
{
if(size == 0){
pp = new char [size+1]; //выделяем память для указателя на строку
}
else{ //если массив уже не пустой, данные надо скопировать во временный массив copy
char
copy = new char
[size+1]; //создаем временный массив
for(int i = 0; i < size; i++) //копируем в него адреса уже определенных строк
{
copy = pp;
}
//теперь строки хранятся в адресах copy

delete [] pp; //освобождаем память, которая указывала на строки

pp = copy; //показываем указателю на какие адреса теперь ссылаться
}

pp[size] = new char [strlen(str) + 1]; //выделяем память на новую строку
strcpy(pp[size], str); //и копируем новую строку в элемент p[size].

return pp;
}
В строке 6 объявляем прототип функции char AddPtr (char pp, int size, char str);. Перед названием функции ставим две звездочки, так как функция будет возвращать указатель на указатель. В главной функции main() все достаточно просто. Создаем указатель на указатель типа char pp, который изначально ни на что не указывает, и счетчик элементов массива указателей size - строки 12-13. Далее (строки 17 — 30) идет поочередное наращивание массива указателей и добавление в него данных, посредством вызова функции AddPtr(). При этом, каждый раз после вызова функции мы увеличиваем значение size на единицу. Теперь переместимся к самому интересному — к определению функции AddPtr() строки 44 — 66. Как уже говорилось выше, в виде параметров функция будет принимать уже объявленный нами указатель на указатель, счетчик элементов массива указателей и определённую нами строку. При первом вызове, в функцию передаётся нулевое значение счетчика size. Срабатывает if (size == 0) (строки 46 — 48) в котором мы выделяем динамическую память для первого элемента массива указателей pp = new char [size+1];. Перед квадратными скобками стоит оператор звездочка *, который показывает компилятору, что нужно выделить динамическую память под один указатель (а не просто под символ char, если бы звездочки не было). If отработал и мы перемещаемся в строку 62. Тут мы «говорим» — пусть 0-й элемент массива указателей (указатель pp[size]) указывает на массив символов размером [strlen(str) + 1] (размер определённой нами строки + 1 символ для '\n'). И следующим логичным шагом будет копирование строки, переданной в функцию, в этот выделенный участок памяти — строка 63. И в завершении работы, функция возвращает в программу указатель на указатель (тот самый указатель, который хранит адрес нулевого элемента массива указателей на строки). И наш, объявленный в main(), char pp теперь будет хранить в себе значение этого адреса, так как вызов функции выглядит так pp = AddPtr(pp, size, "11111111111111111");(присвоить значение, которое вернет функция). Функция отработала — память выделена, данные внесены.
sageАноним 10/05/15 Вск 03:23:14 #192 №92627708 DELETED
Вызываем функцию второй раз — строка 20. При этом вызове уже сработает блок else определённый в строках 49 — 60. У нас уже есть строка, данные которой нам надо не потерять и добавляется еще одна, для которой надо создать новый указатель в массиве указателей, выделить динамическую память и записать туда данные. Поэтому создаем временную копию нашего указателя и выделяем память уже под два элемента массива указателей char copy = new char* [size+1];. Копируем в него указатель на перовую строку (нулевой элемент массива указателей) - copy = pp;. Освобождаем память, которая указывала на первую строку. Так как это массив указателей (пусть даже пока с одним элементом) чтобы освободить занимаемую им память, надо перед именем указателя поставить квадратные скобки — delete [] pp;. Нам эта память больше не нужна, так как на нее уже указывает copy[0]. И показываем указателю pp на какой новый участок памяти надо теперь ссылаться — строка 59. Так — первая строка у нас сохранена и на нее теперь указывает pp[0]. И теперь мы снова переходим к строкам 62 — 63, где выделяется память для второй строки и строка копируется в этот участок памяти.

Таких вызовов функций у нас пять. Постепенно массив указателей растет, а новые строки заполняются данными. Чтобы убедиться, что все работает правильно и все данные сохранены, показываем все строки на экран с помощью цикла for — строки 32-33. Как видите, мы обращаемся к элементам массива указателей. А так как они ссылаются на адреса строк (на 0-е элементы символьных массивов), на экран выводятся соответствующие строки.

Перед завершением работы программы, нам надо освободить память занимаемую строками. Это мы реализуем с помощью цикла:

1
2
3
4
for(int i = 0; i < size; i++)
{
delete [] pp;
}
Так освобождаем динамическую память, на которую ссылаются указатели из массива указателей. А далее освобождаем память, выделенную под сам массив указателей — строка 40.

Результат:

CppStudio.com
~~~~~Добавляем указатели на пять строк и заполняем строки данными~~~~~
11111111111111111
22222222222222222
33333333333333333
44444444444444444
55555555555555555

Условие этой задачи мы выполнили. Надеюсь, вы оценили главное преимущество использования указателей вместо обычных массивов. При входе в программу мы не знаем, сколько строк нам будет необходимо и какой объем памяти они будут занимать. Но мы не объявляли несколько десятков символьных массивов с размером [много памяти] (а вдруг пригодятся, если пользователь будет вводить много длинных строк). Вместо этого у нас получился один динамический массив указателей на строки, память для которых так же выделяется динамически.

Во второй части этой статьи мы добавим в программу еще две функции. Одна будет удалять выбранную нами строку и указатель на нее. Вторая будет вставлять указатель на строку в выбранную нами ячейку массива указателей. Не переживайте. Если вам более менее понятно, что произошло в примере выше, то дальше будет легче всё понять.
sageАноним 10/05/15 Вск 03:23:33 #193 №92627725 DELETED
Литералы используются в тексте программы для обозначения числовых значений, строк, символов или логических констант. Другими словами литерал представляет собой постоянное значение, у которого нет имени.

Целочисленные литералы состоят из цифр от 0 до 9 со знаком +, - или без знака. Например, +25, –7, 553. Литералы с плавающей точкой могут быть записаны в виде целой и дробной частей, разделенных точкой (целая часть может отсутствовать, если она равна 0, если дробная часть равна 0, то после точки должен быть записан 0) или в экспоненциальной форме (вместо основания степени 10 используется e или E). Например, 8.1, 0.2, 3.0, –5.3E–1, 5e4.

Строковые литералы используются для представления текстовых строк. Это строка символов, заключенная в кавычки. Например, «Город Симферополь» или «Результаты вычислений». С помощью символа \ (обратный слеш) можно указать некоторые специальные и управляющие символы. Последовательность и следующего за ним символа называется управляющей последовательностью. Например, наличие в строке последовательности \n означает переход на новую строку, \t – горизонтальная табуляция, \\ — обратный слеш, \" - двойная кавычка и т.п. Символьные литералы используются для представления одиночных символов (заключаются в одинарные кавычки). Например:

1
char symbol = 'x';
Литералы для представления логических значений: true, false. Ключевое слово null является литералом, представляющим пустую ссылку, которая не ссылается ни на один объект.

Практика
sageАноним 10/05/15 Вск 03:23:53 #194 №92627740 DELETED
Допустим, что в некотором месте в вашем коде происходит вызов определенной функции. Зададимся вопросом, при каких условиях при компилировании этого участка кода компилятор не выдаст ошибки. На этот вопрос существует простой ответ. Где-то в том же файле, в котором осуществляется вызов функции, перед операцией, в которой осуществляется вызов, должны присутствовать прототип или описание функции. Кроме того, аргументы и тип возвращаемого значения в вызове должны соответствовать аргументам и типу возвращаемого значения в прототипе и в описании функции.

Итак, что же такое прототип функции? Прототип имеет следующий вид.

1
2
// прототип функции
void function(int arg1, double arg2); // в конце определения всегда ставится точка с запятой
Как видно в прототипе указываются по порядку тип возвращаемого значения (в данном примере void), название функции (в данном случае function) и список параметров в скобках. Объявление прототипа должно заканчиваться точкой с запятой.

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

Для чего нужен именно прототип? Почему нельзя ограничиться использованием одного описания функции? Прототип стал необходим после того, как стандарты языка С изменились таким образом, что перед вызовом функции в файле необходимо каким либо образом ее описать. Проблема состоит в том, что имя функции имеет глобальную область видимости (если ее описание находится вне всяких локальных областей). Допустим, что описание функции находится в отдельном исходном файле. Также допустим, что необходимо осуществить вызов этой же функции в нескольких других исходных файлах. Если нет прототипа, то в каждый такой исходный файл необходимо включить полное описание функции. Компилятор будет интерпретировать это как переопределение. Если же мы используем прототип, то мы можем включать этот прототип в столько исходных файлов, сколько нам необходимо.

Как лучше всего использовать прототип функции. Лучше всего описание функции включить в отдельный исходный файл. После этого надо скомпилировать этот файл и получить объектный файл. Прототип следует помесить в заголовочный файл и включать его директивой #include в те исходные файлы, в которых присутствует вызов функции.
sageАноним 10/05/15 Вск 03:24:18 #195 №92627754 DELETED
Рекурсия достаточно распространённое явление, которое встречается не только в областях науки, но и в повседневной жизни. Например, эффект Дросте, треугольник Серпинского и т. д. Самый простой вариант увидеть рекурсию – это навести Web-камеру на экран монитора компьютера, естественно, предварительно её включив. Таким образом, камера будет записывать изображение экрана компьютера, и выводить его же на этот экран, получится что-то вроде замкнутого цикла. В итоге мы будем наблюдать нечто похожее на тоннель.

В программировании рекурсия тесно связана с функциями, точнее именно благодаря функциям в программировании существует такое понятие как рекурсия или рекурсивная функция. Простыми словами, рекурсия – определение части функции (метода) через саму себя, то есть это функция, которая вызывает саму себя, непосредственно (в своём теле) или косвенно (через другую функцию). Типичными рекурсивными задачами являются задачи: нахождения n!, числа Фибоначчи. Такие задачи мы уже решали, но с использованием циклов, то есть итеративно. Вообще говоря, всё то, что решается итеративно можно решить рекурсивно, то есть с использованием рекурсивной функции. Всё решение сводится к решению основного или, как ещё его называют, базового случая. Существует такое понятие как шаг рекурсии или рекурсивный вызов. В случае, когда рекурсивная функция вызывается для решения сложной задачи (не базового случая) выполняется некоторое количество рекурсивных вызовов или шагов, с целью сведения задачи к более простой. И так до тех пор пока не получим базовое решение. Разработаем программу, в которой объявлена рекурсивная функция, вычисляющая n!

MVS
Code::Blocks
Dev-C++
QtCreator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// factorial.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

unsigned long int factorial(unsigned long int);// прототип рекурсивной функции
int i = 1; // инициализация глобальной переменной для подсчёта кол-ва рекурсивных вызовов
unsigned long int result; // глобальная переменная для хранения возвращаемого результата рекурсивной функцией

int main(int argc, char argv[])
{
int n; // локальная переменная для передачи введенного числа с клавиатуры
cout << "Enter n!: ";
cin >> n;
cout << n << "!" << "=" << factorial(n) << endl; // вызов рекурсивной функции
system("pause");
return 0;
}

unsigned long int factorial(unsigned long int f) // рекурсивная функция для нахождения n!
{
if (f == 1 || f == 0) // базовое или частное решение
return 1; // все мы знаем, что 1!=1 и 0!=1
cout << "Step\t" << i << endl;
i++; // операция инкремента шага рекурсивных вызовов
cout << "Result= " << result << endl;
result = f
factorial(f - 1); // функция вызывает саму себя, причём её аргумент уже на 1 меньше
return result;
}
В строках 7, 9, 21 объявлен тип данных unsigned long int, так как значение факториала возрастает очень быстро, например уже 10! = 3 628 800. Если не хватит размера типа данных, то в результате мы получим совсем не правильное значение. В коде объявлено больше операторов, чем нужно, для нахождения n!. Это сделано для того, чтобы, отработав, программа показала, что происходит на каждом шаге рекурсивных вызовов. Обратите внимание на выделенные строки кода, строки 23, 24, 28 - это рекурсивное решение n!. Строки 23, 24 являются базовым решением рекурсивной функции, то есть, как только значение в переменной f будет равно 1 или 0 (так как мы знаем, что 1! = 1 и 0! = 1), прекратятся рекурсивные вызовы, и начнут возвращаться значения, для каждого рекурсивного вызова. Когда вернётся значение для первого рекурсивного вызова, программа вернёт значение вычисляемого факториала. В строке 28 функция factorial() вызывает саму себя, но уже её аргумент на единицу меньше. Аргумент каждый раз уменьшается, чтобы достичь частного решения. Результат работы программы (см. Рисунок 1).

CppStudio.com
Enter n!: 5
Step 1
Result= 0
Step 2
Result= 0
Step 3
Result= 0
Step 4
Result= 0
5!=120
Рисунок 1 — Рекурсия в С++
sageАноним 10/05/15 Вск 03:24:40 #196 №92627770 DELETED
По результату работы программы хорошо виден каждый шаг и результат на каждом шаге равен нулю, кроме последнего рекурсивного обращения. Необходимо было вычислить пять факториал. Программа сделала четыре рекурсивных обращения, на пятом обращении был найден базовый случай. И как только программа получила решение базового случая, она порешала предыдущие шаги и вывела общий результат. На рисунке 1 видно всего четыре шага потому, что на пятом шаге было найдено частное решение, что в итоге вернуло конечное решение, т. е. 120. На рисунке 2 показана схема рекурсивного вычисления 5!. В схеме хорошо видно, что первый результат возвращается, когда достигнуто частное решение, но никак не сразу, после каждого рекурсивного вызова.

Рекурсивный вызов функции в С++

Рисунок 2 — Рекурсия в С++

Итак, чтобы найти 5! нужно знать 4! и умножить его на 5; 4! = 4 3! и так далее. Согласно схеме, изображённой на рисунке 2, вычисление сведётся к нахождению частного случая, то есть 1!, после чего по очереди будут возвращаться значения каждому рекурсивному вызову. Последний рекурсивный вызов вернёт значение 5!.

Переделаем программу нахождения факториала так, чтобы получить таблицу факториалов. Для этого объявим цикл for, в котором будем вызывать рекурсивную функцию.

MVS
Code::Blocks
Dev-C++
QtCreator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// factorial.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

unsigned long int factorial(unsigned long int);// прототип рекурсивной функции
int i = 1; // инициализация глобальной переменной для подсчёта кол-ва рекурсивных вызовов
unsigned long int result; // глобальная переменная для хранения возвращаемого результата рекурсивной функцией

int main(int argc, char
argv[])
{
int n; // локальная переменная для передачи введенного числа с клавиатуры
cout << "Enter n!: ";
cin >> n;
for (int k = 1; k <= n; k++ )
{
cout << k << "!" << "=" << factorial(k) << endl; // вызов рекурсивной функции
}
system("pause");
return 0;
}

unsigned long int factorial(unsigned long int f) // рекурсивная функция для нахождения n!
{
if (f == 1 || f == 0) // базовое или частное решение
return 1; // все мы знаем, что 1!=1 и 0!=1
//cout << "Step\t"<< i <<endl;
i++;
//cout <<"Result= "<< result << endl;
result=ffactorial(f-1); // функция вызывает саму себя
return result;
}

В строках 16 — 19 объявлен цикл, в котором вызывается рекурсивная функция. Всё ненужное в программе закомментированно. Запустив программу, нужно ввести значение, до которого необходимо вычислить факториалы. Результат работы программы показан на рисунке 3.

CppStudio.com
Enter n!: 14
1!=1
2!=2
3!=6
4!=24
5!=120
6!=720
7!=5040
8!=40320
9!=362880
10!=3628800
11!=39916800
12!=479001600
13!=6227020800
14!=87178291200
Рисунок 3 — Рекурсия в С++

Теперь видно, насколько быстро возрастает факториал, кстати говоря, уже результат 14! не правильный, это и есть последствия нехватки размера типа данных. Правильное значение 14! = 87178291200.

Рассмотрим ещё одну типичную задачу — нахождение чисел Фибоначчи, используя рекурсию. Далее приведен код рекурсивного решения такой задачи. Вводим в ком строке порядковый номер числа из ряда Фибоначчи, и программа найдёт все числа из ряда Фибоначчи порядковые номера которых меньше либо равны введённого.

MVS
Code::Blocks
Dev-C++
QtCreator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// fibonacci.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
// библиотека для форматирования выводимой информации на экран
#include <iomanip>
using namespace std;

unsigned long fibonacci(unsigned long);// прототип рекурсивной функции поиска чисел из ряда Фибоначчи

int main(int argc, char
argv[])
{
unsigned long entered_number;
cout << "Enter number from the Fibonacci series: ";
cin >> entered_number;
for (int counter = 1; counter <= entered_number; counter++ )
cout << setw(2) <<counter << " = "<< fibonacci(counter) << endl; // функция setw() резервирует два места под вывод номера
system("pause");
return 0;
}

unsigned long fibonacci(unsigned long entered_number) // функция принимает один аргумент
{
if ( entered_number == 1 || entered_number == 2) // частный случай
return (entered_number -1); // ряд чисел Фибоначчи всегда начинается с 0, 1, ...
return fibonacci(entered_number-1) + fibonacci(entered_number-2); // формула поиска н-го числа, например найти восьмое по счёту число, и оно равно 7-е + 6-е
}
В строке 6 подключена библиотека <iomanip> для того, чтобы воспользоваться функцией setw(), которая в свою очередь выравнивает первый столбец чисел, то есть номера. Как мы можем заметить, сначала числа однозначные от 1 – 9, а потом идут двузначные. Если убрать данную функцию, то произойдет сдвиг влево чисел от 1 и до 9. Результат работы программы (см. Рисунок 4).

CppStudio.com
Enter number from the Fibonacci series: 30
1 = 0
2 = 1
3 = 1
4 = 2
5 = 3
6 = 5
7 = 8
8 = 13
9 = 21
10 = 34
11 = 55
12 = 89
13 = 144
14 = 233
15 = 377
16 = 610
17 = 987
18 = 1597
19 = 2584
20 = 4181
21 = 6765
22 = 10946
23 = 17711
24 = 28657
25 = 46368
26 = 75025
27 = 121393
28 = 196418
29 = 317811
30 = 514229
Рисунок 4 — Рекурсия в С++

Решение сводится к разбиению сложной задачи к двум более простым. Например, чтобы найти третье число из ряда Фибоначчи, необходимо сначала найти первое и второе, а потом сложить их. Первое число является частным случаем и равно 0 (нулю), второе число также является частным случаем и равно 1. Следовательно, третье число из ряда Фибоначчи равно сумме первого и второго = 1. Приблизительно так же рассуждала запрограммированная нами рекурсивная функция поиска чисел ряда Фибоначчи.

Разработаем ещё одну рекурсивную программу, решающую классическую задачу — «Ханойская башня». Даны три стержня, на одном из которых находится стопка n-го количества дисков, причём диски имеют не одинаковый размер (диски различного диаметра) и расположены таким образом, что по мере прохождения, сверху вниз по стержню диаметр дисков постепенно увеличивается. То есть диски меньшего размера должны лежать только на дисках большего размера. Необходимо переместить эту стопку дисков с начального стержня на любой другой из двух оставшихся (чаще всего это третий стержень). Один из стержней использовать как вспомогательный. Перемещать можно только по одному диску, при этом диск большего размера никогда не должен находиться над диском меньшего размера.

Допустим необходимо переместить три диска с первого стержня на третий, значит второй стержень вспомогательный. Визуальное решение данной задачи реализовано во Flash. Нажмите на кнопку start, чтобы запустить анимацию, кнопку stop, чтобы остановить.



Программу надо написать для n-го количества дисков. Так как мы решаем данную задачу рекурсивно, то для начала необходимо найти частные случаи решения. В данной задаче частный случай только один – это когда необходимо переместить всего один диск, и в этом случае даже вспомогательный стержень не нужен, но на это просто не обращаем внимания. Теперь необходимо организовать рекурсивное решение, в случае, если количество дисков больше одного. Введём некоторые обозначения, для того, чтоб не писать лишнего:

<Б> - стержень, на котором изначально находятся диски (базовый стержень);
<П> - вспомогательный или промежуточный стержень;
<Ф> - финальный стержень – стержень, на который необходимо переместить диски.

Далее, при описании алгоритма решения задачи будем использовать эти обозначения. Чтобы переместить три диска с <Б> на<Ф> нам необходимо сначала переместить два диска с <Б> на <П> а потом переместить третий диск(самый большой) на<Ф>, так как <Ф> свободен.

Для того, чтобы переместить n дисков с <Б> на <Ф> нам необходимо сначала переместить n-1 дисков с <Б> на <П> а потом переместить n-й диск(самый большой) на <Ф>, так как <Ф> свободен. После этого необходимо переместить n-1 дисков с <П> на <Ф>, при этом использовать стержень <Б> как вспомогательный. Эти три действия и есть весь рекурсивный алгоритм. Этот же алгоритм на псевдокоде:
n-1 переместить на <П>
n переместить на <Ф>
n-1 переместить с <П> на <Ф>, при этом использовать <Б> как вспомогательный
sageАноним 10/05/15 Вск 03:25:01 #197 №92627784 DELETED
MVS
Code::Blocks
Dev-C++
QtCreator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// hanoi_tower.cpp: определяет точку входа для консольного приложения.
// Программа, рекурсивно решающая задачу "Ханойская башня"

#include "stdafx.h"
#include <iostream>
#include <iomanip>
using namespace std;

void tower(int, int, int, int); // объявление прототипа рекурсивной функции
int count = 1; // глобальная переменная для подсчёта количества шагов

int _tmain(int argc, _TCHAR* argv[])
{
cout << "Enter of numbers of disks: ";// введите количество дисков, которые надо переместить
int number;
cin >> number;
cout << "Enter the number of basic rod: "; // введите номер стержня, на котором диски будут находится изначально
int basic_rod;
cin >> basic_rod;
cout << "Enter the number of final rod: "; // введите номер стержня, на который необходимо переместить диски
int final_rod;
cin >> final_rod;
int help_rod;
// блок определения номера вспомогательного стержня, анализируя номера начального и финального стержня
if (basic_rod != 2 && final_rod != 2 )
help_rod = 2;
else
if (basic_rod != 1 && final_rod != 1 )
help_rod = 1;
else
if (basic_rod != 3 && final_rod != 3 )
help_rod = 3;
tower(// запуск рекурсивной функции решения задачи Ханойских башен
number, // переменная, хранящая количество дисков, которые надо переместить
basic_rod, // переменная, хранящая номер стержня, на котором диски будут находится изначально
help_rod , // переменная, хранящая номер стержня, который используется, как вспомогательный
final_rod); // переменная, хранящая номер стержня, на который необходимо переместить диски
system("pause");
return 0;
}

void tower(int count_disk, int baza, int help_baza, int new_baza)
{
if ( count_disk == 1) // условие завершения рекурсивных вызовов
{
cout << setw(2) << count << ") "<< baza << " " << "->" << " " << new_baza << endl;
count++;
}
else
{
tower(count_disk -1, baza, new_baza, help_baza); // перемещаем все диски кроме самого последнего на вспомогательный стержень
tower(1, baza, help_baza, new_baza); // перемещаем последний диск с начального стержня на финальный стержень
tower(count_disk -1, help_baza, baza, new_baza); // перемещаем все диски со вспомогательного стержня на финальный
}
}
На рисунке 5 показан пример работы рекурсивной программы Ханойская башня. Сначала мы ввели количество дисков равное трём, потом ввели базовый стержень (первый), и обозначили конечный стержень (третий). Автоматически второй стержень стал вспомогательным. Программа выдала такой результат, что он полностью совпадает с анимационным решением данной задачи.

CppStudio.com
Enter of numbers of disks: 3
Enter the number of basic rod: 1
Enter the number of final rod: 3
1) 1 -> 3
2) 1 -> 2
3) 3 -> 2
4) 1 -> 3
5) 2 -> 1
6) 2 -> 3
7) 1 -> 3
Рисунок 5 — Рекурсия в С++

Из рисунка видно, что сначала перемещается диск со стержня один на стержень три, потом со стержня один на стержень два, со стержня три на стержень два и т. д. То есть программа всего лишь выдает последовательность перемещений дисков и минимальное количество шагов, за которые будут перемещены все диски.

Все эти задачи можно было решить итеративно. Возникает вопрос: “Как лучше решать, итеративно или рекурсивно?”. Отвечаю: “Недостаток рекурсии в том, что она затрачивает значительно больше компьютерных ресурсов, нежели итерация. Это выражается в большой нагрузке, как на оперативную память, так и на процессор. Если очевидно решение той или иной задачи итеративным способом, то им и надо воспользоваться иначе, использовать рекурсию!” В зависимости от решаемой задачи сложность написания программ изменяется при использовании того или иного метода решения. Но чаще задача, решённая рекурсивным методом с точки зрения читабельности кода, куда понятнее и короче.
sageАноним 10/05/15 Вск 03:25:23 #198 №92627805 DELETED
В этой статье мы расскажем о следующем:

Что такое дружественные функции и, главное, зачем они нужны.
Чем отличаются обычные методы класса от дружественных функций класса.
Как объявлять и определять дружественные функции.
Как вызывать дружественные функции из главной main() функции .
Самым важным, но и, скорее всего, самым непонятным для вас сейчас станет определение дружественной функции. Дружественная функция — это функция, которая не является членом класса, но имеет доступ к членам класса, объявленным в полях private или protected. Долго не вникайте в суть этого определения, а лучше сразу переходите к следующему абзацу. Обещаю, что после прочтения статьи вы вернетесь к этому определению и вас посетит мысль: «Ну да — так и есть! Тут все понятно!»

Приступим к основному занятию программиста — практике! В примере запрограммируем следующее: создадим класс Woman25, который, используя дружественные функции и обычные методы класса, будет получать данные об объекте (имя и вес) и выводить их на экран. Методы и friend-функции будут выполнять аналогичные действия. В этом и есть особенная польза данного примера — вы сможете посмотреть отличия в объявлении и определении дружественных функций от обычных методов класса. На основании полученных данных, программа даст пользователю совет относительно корректировки его веса. Ну что-же, лучше один раз увидеть…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <string.h>

using namespace std;

class Woman25
{
private:
char name;//имя
int weight;//вес

friend void setData(char
, int, Woman25&);//объявление дружественных функций
friend void getData(Woman25&);
public:
Woman25()//конструктор
{
name = new char [20];
strcpy(name, "Норма");
weight = 60;
}
~Woman25()//деструктор
{
delete [] name;
cout << "!!! Деструктор !!!" << endl;
}

void setData(char, int);//объявление методов класса
void getData();
void advise();
};

void setData(char
n, int w, Woman25& object)//определяем friend-функцию setData
{
strcpy(object.name, n);////////////
object.weight = w;
}

void getData(Woman25& object)//определяем friend-функцию getData
{
cout << object.name << "\t: " << object.weight << " кг" << endl;
}

void Woman25::setData(char n, int w)//определяем set-метод класса
{
strcpy(name, n);
weight = w;
}

void Woman25::getData()//определяем get-метод класса
{
cout << name << "\t: " << weight << " кг" << endl;
}

void Woman25::advise()//определяем метод класса Совет (advise)
{
if(weight < 55){ //если вес меньше 55 кг
cout << "Вам надо потреблять больше калорий!" << endl;
cout << "=====================================" << endl << endl;
}else if(weight >= 55 && weight <= 65){ //если вес в пределах 55-65 кг
cout << "Ваш вес в норме!" << endl;
cout << "=====================================" << endl << endl;
}else { //если вес > 65 кг
cout << "Вам надо ограничивать себя в еде!" << endl;
cout << "=====================================" << endl << endl;
}
}

int main()
{
setlocale(LC_ALL, "rus");

Woman25 Norm; //создаем объект Norm, сработает конструктор и weight будет = 60, name - Норма
Norm.getData(); //вызов метода класса
cout << "=====================================" << endl << endl;

Woman25 Anna; //второй объект
Anna.setData("Анна", 100);
Anna.getData();
Anna.advise();

Woman25 Inna; //третий объект
setData("Инна", 50, Inna);
getData(Inna);
Inna.advise();

return 0;
}
Комментарии к исходному коду. В строках 6 — 30 создаем класс Woman25. Он содержит два приватных элемента класса: char
name; и int weight;. В private также объявим дружественные функции friend void setData(char *, int, Woman25&); и friend void getData(Woman25&);. Хотя их можно объявлять и определять в любом поле класса, будь то private, public или protected, но об этом поговорим позже. Так мы сделали, чтобы показать, что несмотря на объявление дружественных функций в поле private, к ним можно обращаться напрямую из функции main(). Тогда, когда методы класса должны располагаться в поле public всегда, если мы собираемся вызывать их вне класса. Объявление дружественных функций отличается от объявления методов класса еще и тем, что перед типом возвращаемого функцией значения используется зарезервированное слово friend. Обратите внимание, что в виде параметра мы передаем этим функциям ссылку на объект нашего класса - Woman25&. В обычных методах этого не будет. В поле public расположился конструктор класса — строки 15 — 20. В нем мы задаем значение нормального веса - 60 кг, а так же, с помощью функции strcpy(name, "Норма");, вносим данные в элемент класса name. Затем в строках 21 — 25 определяем деструктор класса ~Woman25(). Его задача, освободить, выделенную конструктором, динамическую память. А для того, чтобы убедиться, что он сработал и удалил динамическую память всех созданных объектов класса при завершении работы программы, мы добавили в него строку cout << "!!! Деструктор !!!" << endl; . Строки 27 — 29 - объявление обычных методов класса.

Теперь начинается, пожалуй, самое интересное — определение вне класса наших дружественных функций и обычных методов класса. Все это располагается в строках 32 - 66. Смотрите, когда мы определяем дружественные функции , строки 32 — 36 и 38 — 41, мы не используем оператор :: двойное двоеточие (область видимости метода). Это уже говорит нам о том, что дружественная функция не принадлежит классу, не является его компонентом. А при определении остальных методов использование оператора :: является обязательным. Метод класса advise(), на основании полученных данных о весе, дает пользователю один из советов либо сообщает, что вес в норме.

Переходим в главную функцию — строки 68 — 87. Тут мы создаем объект класса Woman25 Norm;, при создании которого сработает конструктор и инициализирует элементы name и weight. Вызываем метод класса Norm.getData(); чтобы вывести на экран значение нормы. Со вторым созданным объектом Woman25 Anna; работаем, вызывая обычные set и get- методы класса, а с третьим объектом Woman25 Inna; — вызывая дружественные функции. Как видите, вызываются они как функции, которые не принадлежат классу. Объект класса мы передаем, как параметр.
sageАноним 10/05/15 Вск 03:25:44 #199 №92627817 DELETED
Запускаем программу и видим следующее.

CppStudio.com
Норма : 60 кг
=====================================

Анна : 100 кг
Вам надо ограничивать себя в еде!
=====================================

Инна : 50 кг
Вам надо потреблять больше калорий!
=====================================

!!! Деструктор !!!
!!! Деструктор !!!
!!! Деструктор !!!

Для продолжения нажмите любую клавишу . . .

И friend-функции, и методы класса справились со своими задачами, а мы с вами увидели отличия в их объявлении и определении. Так же мы увидели, что деструктор класса, как положено, сработал 3 раза (т.е. при уничтожении каждого созданного объекта). Если у вас возникает вопрос «Собственно, а зачем все это, если методы класса и так прекрасно работают?» — это хорошо! Ответ такой — «Представьте, что у нас есть еще десяток классов. Например Girl6_7, Girl8_9, Man25 и т.д., Используя дружественные функции, нам не придется для каждого класса определять set и get-методы. Это в нашей программе они короткие! А если бы они занимали 20 — 30 строк? А так достаточно определить метод в одном из классов или вообще определить функцию, как глобальную, а в остальные классы прописать ее прототип, как дружественной функции (используя слово friend). Мы экономим массу времени и наш код становится намного короче.»

Теперь, как всегда, немного теории, для закрепления материала:

Дружественная функция может располагаться в любом поле класса – private, public или protected. Она при любых обстоятельствах будет иметь доступ к private-элементам класса и, даже если она сама находится в поле private (как в нашем примере), к ней можно будет обратиться вне класса, не используя специальных методов.
Когда мы определяем дружественную функцию, элементы класса необходимо явно передавать в нее в виде параметров функции. Так как она не является компонентом класса, она не получает указатель this.
В виде параметра, в дружественную функцию так же надо передать указатель или ссылку на объект класса. Иначе она не увидит данные какого класса ей принять и обработать.
Функция может использоваться, как дружественная к нескольким классам.
Вызываются дружественные функции, как обычные функции. Т.е не используется такой способ - Объект_класса.функция(). После внесения всех необходимых параметров в нее при вызове, она сама увидит с элементами какого класса и объекта надо работать.
Вот и все для начала. Вопросы, предложения и замечания по теме ждем в комментариях к этой статье.
sageАноним 10/05/15 Вск 03:26:12 #200 №92627833 DELETED
Приветствую вас коллеги – программисты. В данной статье я хочу познакомить Вас с шаблонным классом string. Который призван облегчить жизнь программистам. Кто не знает, string – это STL’евский класс основанный на шаблонах, который входит в стандартную библиотеку C++. Для использования данного класса в ваших приложениях, нужно подключить директиву <string>.

Вначале посмотрим на простой пример применения, возможности и внутреннее представление класса, а в конце статьи реализуем собственный класс string!

Если вы раньше уже имели дело со стандартным строковым типом, то могли заметить, что его применение опасно возникновением ошибок и не так удобно как хотелось бы из-за того, что реализован он на более низком уровне. Так вот, использование класса string, здорово облегчит вам процесс использования строковых типов в ваших приложениях.

Давайте рассмотрим простейший пример использования:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>

int main()
{
std::string str;

std::cout << "Enter your name: ";
std::getline(std::cin, str);

std::cout << "Hello, " << str << "!!! \n";

return 0;
}
Здесь создается экземпляр класса под именем str, далее пользователя просят представится, при помощи std::getline данные записываются из потока cin непосредственно в нашу переменную str. Думаю далее всё очевидно.

Как видите использование класса string, не сложнее, а поверьте даже намного легче чем обычного строкового типа char.

Итак не забывайте что, для того чтобы использовать объекты класса string, необходимо включить соответствующий заголовочный файл:

1
#include <string>
Далее для демонстрации работы методов и вспомогательных функций будем использовать следующий объект:

1
std::string str;
Чтобы узнать длину строки, можно воспользоваться функцией-членом size(), или length(), которые, как и ожидается, возвращают саму длину (длина не включает завершающий нулевой символ).

1
2
str.size();
str.length();
А как же узнать, пуста ли строка? Вы скажете у нас же есть метод возвращающий длину, следовательно можно применить следующую конструкцию:

1
if (str.size() == 0) // Если условие истинно, то строка пуста.
Однако и для таких случаев имеется специальный метод empty(), возвращающий true для пустой строки и false для непустой:

1
str.empty();
Как мы можем узнать, совпадают ли строки? Воспользуемся оператором сравнения ==:

1
if (str == str2)
А как же можно скопировать одну строку в другую? Это можно реализовать с помощью обычной операции присваивания =:

1
2
std::string str2; // Создание объекта строки
str2 = str; // при помощи оператора присваивания, копируем str в str2
Возможно вы спросите почему я не сделал так?:

1
std::string str2 = str;
Эта запись эквивалентна предыдущей, за исключением того, что в предыдущем коде мы использовали оператор присваивания, а этом случае работает конструктор копирования.

Для конкатенации строк используется операция сложения + или операция сложения с присваиванием +=. Рассмотрим следующий код:

1
2
std::string str1 = "Hello";
std::string str2 = "World";
Мы можем получить строку "HelloWorld", состоящую из конкатенации str1 и str2, таким образом:

1
std::string str3 = str1 + str2;
В результате в строке str3 будет хранится следующее: "HelloWorld".

Так же подобного результата, мы можем добиться и другим способом:

1
2
std::string str3 = str1;
str3 += str2;
Здесь мы создаем строку str3 инициализируя её содержимым str1, и далее при помощи оператора += в конец str3 добавляем str2. Результат будет таким как и в предыдущем примере.

Операция сложения может конкатенировать (соединять, присоединять) экземпляры (объекты) класса std::string не только между собой, но и со строками встроенного типа. Такой код будет вполне работоспособным:

1
2
3
4
char
str = "Hello";
std::string str2 = str;
str2 += " World";
str2 += '!';
Надеюсь как вы уже догадались в результате в строке str2, будет хранится "HelloWorld!"

Ещё одна весьма полезная функция это: c_str().

Функция c_str() возвращает указатель на символьный массив, который содержит строку объекта стринг (string) в том виде, в котором она размещалась бы, во встроенном строковом типе. Например:

1
2
std::string str;
const char* str2 = str.c_str();
Чтобы обращаться к отдельным символам строки типа string, можно воспользоваться операцией взятия индекса ([]), или при помощи метода at(). Например:

1
2
std::string str = "Hello World";
std::cout << str[7] << str[0] << std::endl; // На консоли увидим: oH
Метод at() предлагает похожую схему доступа, за исключением того, что индекс предоставляется как аргумент функции:

1
2
std::string str = "Hello World";
std::cout << str.at(7) << str.at(0) << std::endl; // На консоли увидим: oH
В отличии от оператора [], метод at(), обеспечивает проверку границ и генерирует исключение out_of_range, если вы пытаетесь получить несуществующий элемент.

Так же в классе string, имеется функция, которая возвращает строку, являющуюся подстрокой исходной строки, начиная с позиции pos и включая n символов, или до конца строки.
sageАноним 10/05/15 Вск 03:26:30 #201 №92627847 DELETED
str.substr(pos, n);
На самом деле std::string имеет намного больше методов и возможностей, чем было рассмотрено здесь, и если бы я решил описать хотя бы половину, то вся эта информация никак не поместилась бы в одну статью. Но основные моменты работы со стрингом мы рассмотрели и новичкам будет достаточно и этих знаний.

Полное представление класса string (конструкторы, перегруженные операции, методы, дополнительные функции, и т.д), можно посмотреть здесь: http://www.cplusplus.com/reference/string/basic_string Стандартный класс string основан на таком определении шаблона:

1
2
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string {...};
Здесь charT представляет тип, который хранится в строке. Параметр traits представляет класс, определяющий необходимые свойства, которыми должен обладать тип для представления строки.

Например метод length(), который возвращает длину строки, представленную в виде массива типа charT. Конец такого массива указан значением charT(0), которое является обобщенной формой нулевого символа. Параметр Allocator представляет класс для управления распределением памяти под строку. Шаблон по умолчанию allocator<charT> использует операции new и delete стандартными способами.

Как и любой более-менее сложный пользовательский тип (класс), класс string определяет свои конструкторы. Да да не конструктор, а конструкторы. Их в нём 11.

Вот часть из них:

1
2
3
4
5
explicit basic_string(constAllocator& a = Allocator());
basic_string(const charT s, constAllocator& a = Allocator());
basic_string(const charT
s, size_type n, const Allocator& a = Allocator());
basic_string(const basic_string& str);
basic_string(const basic_string& str, const Allocator&);
Думаю пока нет смысла приводить здесь полный листинг реализации класса string, так как данная статья рассчитана на начинающих программистов, и в ней я планирую скорее научить вас использовать string, нежели объяснять его внутреннее представление. Я привёл здесь определение шаблона и описание некоторых конструкторов, только чтобы показать вам, что это действительно класс. Который можете реализовать и вы (возможно не такой эффективный и гибкий).

Ну что же, настало время реализовать свой простенький класс string. Но перед этим, не забывайте, что класс string обладает всеми возможностями стандартных типов. Если вы знакомы с классами, то наверное уже догадались, что для реализации таких возможностей, в классе должны быть перегружены многие операторы (в том числе ввода >> и вывода <<). Кроме того класс string содержит ещё множество полезных методов.

Итак, код нашего класса String выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#ifndef STRING_H
#define STRING_H

#include <iostream>

namespace STD
{

int StrLen(char);
void StrCpy(char
, char);
bool StrCmp(char
, char);

class String
{
public:
String(char
_str = "");
String(const String&);

~String();

String& operator=(const String&);
friend String operator+(const String&, const String&);
String& operator+=(const String&);

friend bool operator==(const String&, const String&);
friend bool operator!=(const String&, const String&);
friend bool operator>(const String&, const String&);
friend bool operator>=(const String&, const String&);
friend bool operator<(const String&, const String&);
friend bool operator<=(const String&, const String&);

const char& operator[](int) const;
char& operator[](int);

friend std::ostream& operator<<(std::ostream&, const String&);
friend std::istream& operator>>(std::istream&, String&);

private:
char str;
};

String::String(char
_str)
{
str = new char[StrLen(_str)+1];
StrCpy(str, _str);
}

String::String(const String& rhs)
{
str = new char[StrLen(rhs.str)+1];
StrCpy(str, rhs.str);
}

String::~String()
{
delete str;
}

// ---

String& String::operator=(const String& rhs)
{
if (this != &rhs)
{
delete[] this->str;
this->str = new char[StrLen(rhs.str)+1];
StrCpy(this->str, rhs.str);
}

return this;
}

String& String::operator+=(const String& rhs)
{
int sz = StrLen(this->str) + StrLen(rhs.str);

char
ts = new char[sz+1];

for (int i = 0; i < StrLen(this->str); i++)
ts = this->str;
for (int ii = StrLen(this->str), j = 0; ii <= sz; ii++, j++)
ts[ii] = rhs.str[j];

delete this->str;
this->str = ts;

return this;
}

String operator+(const String& lhs, const String& rhs)
{
String ts = lhs;

return ts += rhs;
}

// --

bool operator==(const String& lhs, const String& rhs)
{
return StrCmp(lhs.str, rhs.str);
}

bool operator!=(const String& lhs, const String& rhs)
{
return !(StrCmp(lhs.str, rhs.str));
}

bool operator>(const String& lhs, const String& rhs)
{
return (StrLen(lhs.str) > StrLen(rhs.str)) ? true : false;
}

bool operator>=(const String& lhs, const String& rhs)
{
return (StrLen(lhs.str) >= StrLen(rhs.str)) ? true : false;
}

bool operator<(const String& lhs, const String& rhs)
{
return (StrLen(lhs.str) < StrLen(rhs.str)) ? true : false;
}

bool operator<=(const String& lhs, const String& rhs)
{
return (StrLen(lhs.str) <= StrLen(rhs.str)) ? true : false;
}

// ---

const char& String::operator[](int i) const
{
//std::cerr << "Index out of range. \n";
return (i >= 0 && i < StrLen(this->str)) ? this->str : 0;
}

char& String::operator[](int i)
{
static char DUMMY; DUMMY = '';
//std::cerr << "Index out of range. \n";
return (i >= 0 && i < StrLen(this->str)) ? this->str : DUMMY;
}

// ---

std::ostream& operator<<(std::ostream& os, const String& obj)
{
return os << obj.str;
}

std::istream& operator>>(std::istream& is, String& obj)
{
char BUFF[2048];

is.getline(BUFF, sizeof BUFF);
obj = BUFF;

return is;
}

// ---

int StrLen(char
_str)
{
int size = 0;

for (; _str[size] != 0; size++);

return size;
}

void StrCpy(char in_str, char src_str)
{
for (int i = 0; i < StrLen(in_str); i++)
in_str = src_str;
}

bool StrCmp(char str_f, char str_s)
{
int i = 0;

for (; str_f == str_s && i < StrLen(str_f); i++);

return (i == StrLen(str_f)) ? true : false;
}

}

#endif
Безусловно этот класс, не может сравнится со стандартным, но всё же он вполне работоспособный и его также можно использовать в своих приложениях. Ну вот и всё, что я хотел рассказать вам в этой статье, помните как я уже говорил, std::string, обладает намного большими возможностями, чем было рассмотрено в данной статье. По большому счёту, я не упоминал о многих (возможностях), таки из-за того, что эта статья рассчитанная прежде всего для новичков. Удачи в ваших начинаниях!

P. S.: Если вы страдаете алкоголизмом и не знаете как вылечится от этого, возможно вам поможет статья о принудительном лечении алкоголиков. Кроме того, вы можете задать любой вопрос об алкоголизме на сайте nc-renessans.com и сразу-же получите развернутый ответ.
Аноним 10/05/15 Вск 03:26:47 #202 №92627858 
Ну вот. А я уже думал, что тут 200 постов трапообсуждений и ссылок на годноту. Ну да ладно, и 70 более, чем достаточно, схороняю тред.
sageАноним 10/05/15 Вск 03:27:00 #203 №92627871 DELETED
Сортировка пузырьком – простейший алгоритм сортировки, применяемый чисто для учебных целей. Практического применения этому алгоритму нет, так как он не эффективен, особенно если необходимо отсортировать массив большого размера. К плюсам сортировки пузырьком относится простота реализации алгоритма.

Алгоритм сортировки пузырьком сводится к повторению проходов по элементам сортируемого массива. Проход по элементам массива выполняет внутренний цикл. За каждый проход сравниваются два соседних элемента, и если порядок неверный элементы меняются местами. Внешний цикл будет работать до тех пор, пока массив не будет отсортирован. Таким образом внешний цикл контролирует количество срабатываний внутреннего цикла Когда при очередном проходе по элементам массива не будет совершено ни одной перестановки, то массив будет считаться отсортированным. Чтобы хорошо понять алгоритм, отсортируем методом пузырька массив, к примеру, из 7 чисел (см. Таблица 1).
исходный массив: 3 3 7 1 2 5 0

Таблица 1 — Сортировка пузырьком
№ итерации Элементы массива Перестановки
исх. массив 3 3 7 1 2 5 0
0 3 3 false
1 3 7 false
2 1 7 7>1, true
3 2 7 7>2, true
4 5 7 7>5, true
5 0 7 7>0, true
тек. массив 3 3 1 2 5 0 7
0 3 3 false
1 1 3 3>1, true
2 2 3 3>2, true
3 0 3 3>0, true
4 3 5 false
5 5 7 false
тек. массив 3 1 2 0 3 5 7
0 1 3 3>1, true
1 2 3 3>2, true
2 0 3 3>0, true
3 3 3 false
4 3 5 false
5 5 7 false
тек. массив 1 2 0 3 3 5 7
1 2 false
0 2 2>0, true
2 3 false
3 3 false
3 5 false
5 7 false
тек. массив 1 0 2 3 3 5 7
0 1 1>0, true
1 2 false
2 3 false
3 3 false
3 5 false
5 7 false
конечный массив 0 1 2 3 3 5 7
Конец сортировки
Для того чтобы отсортировать массив хватило пяти запусков внутреннего цикла, for. Запустившись, цикл for срабатывал 6 раз, так как элементов в массиве 7, то итераций (повторений) цикла for должно быть на одно меньше. На каждой итерации сравниваются два соседних элемента массива. Если текущий элемент массива больше следующего, то меняем их местами. Таким образом, пока массив не будет отсортирован, будет запускаться внутренний цикл и выполняться операция сравнения. Обратите внимание на то, что за каждое полное выполнение цикла for как минимум один элемент массива находит своё место. В худшем случае, понадобится n-2 запуска внутреннего цикла, где n – количество элементов массива. Это говорит о том, что сортировка пузырьком крайне не эффективна для больших массивов.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// bu_sort.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;

void bubbleSort(int , int); // прототип функции сортировки пузырьком

int main(int argc, char
argv[])
{
srand(time(NULL));
setlocale(LC_ALL, "rus");
cout << "Введите размер массива: ";
int size_array; // длинна массива
cin >> size_array;

int sorted_array = new int [size_array]; // одномерный динамический массив
for (int counter = 0; counter < size_array; counter++)
{
sorted_array[counter] = rand() % 100; // заполняем массив случайными числами
cout << setw(2) << sorted_array[counter] << " "; // вывод массива на экран
}
cout << "\n\n";

bubbleSort(sorted_array, size_array); // вызов функции сортировки пузырьком

for (int counter = 0; counter < size_array; counter++)
{
cout << setw(2) << sorted_array[counter] << " "; // печать отсортированного массива
}
cout << "\n";

system("pause");
return 0;
}

void bubbleSort(int
arrayPtr, int length_array) // сортировка пузырьком
{
int temp = 0; // временная переменная для хранения элемента массива
bool exit = false; // болевая переменная для выхода из цикла, если массив отсортирован

while (!exit) // пока массив не отсортирован
{
exit = true;
for (int int_counter = 0; int_counter < (length_array - 1); int_counter++) // внутренний цикл
//сортировка пузырьком по возрастанию - знак >
//сортировка пузырьком по убыванию - знак <
if (arrayPtr[int_counter] > arrayPtr[int_counter + 1]) // сравниваем два соседних элемента
{
// выполняем перестановку элементов массива
temp = arrayPtr[int_counter];
arrayPtr[int_counter] = arrayPtr[int_counter + 1];
arrayPtr[int_counter + 1] = temp;
exit = false; // на очередной итерации была произведена перестановка элементов
}
}
}
Результат работы программы показан на рисунке 1.
Аноним 10/05/15 Вск 03:27:34 #204 №92627889 
>>92627858
Я завтра пересоздам вечерком, приходи.
sageАноним 10/05/15 Вск 03:27:38 #205 №92627894 DELETED
Решая задачи, многим из нас приходилось сталкиваться с тем, что нам просто не хватало размерностей типов для, казалось, простейших операций: сложение, вычитание и умножение. Все эти операции знакомы Вам с ранних классов. Но что делать, если одну из этих операций необходимо применить для огромных чисел, скажем так 1000 знаков или более…(насколько хватит фантазии!). Данная статья поможет решить эту проблему.

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

Сложение длинных чисел

Рассмотрим арифметическую операцию сложения, применяемую в длинной арифметике. Алгоритм этого не хитрого арифметического действия, на удивление, простой. Он выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// определяем длину массива суммы
if (size_a > size_b)
length = size_a + 1;
else
length = size_b + 1;

for (int ix = 0; ix < length; ix++)
{
b[ix] += a[ix]; // суммируем последние разряды чисел
b[ix + 1] += (b[ix] / 10); // если есть разряд для переноса, переносим его в следующий разряд
b[ix] %= 10; // если есть разряд для переноса он отсекается
}

if (b[length - 1] == 0)
length--;
“Виновники торжества” – то есть числа, которые мы будем складывать, предположительно записаны в массивы a и b. Необходимо учесть, что они записаны “зеркально”, то есть первый элемент массива соответствует последней цифре соответствующего числа, второй элемент – предпоследней, и т.д. Размеры длин чисел хранятся в переменных size_a и size_b, но Вы можете использовать любые другие. Вы конечно же задумались: “Зачем здесь оператор выбора if, строки 2 — 5?” или “Для чего он здесь?”. В этом блоке кода мы определяем максимальную длину числа, полученного в результате суммирования. Ведь, чаще всего суммируемые числа, разной длинны, одно больше, другое меньше, а нам нужно выделить память так, чтобы каждое число вместилось.

Далее, в алгоритме, делаем так, как нас учили на уроках математики: сначала складываем отдельные разряды, начиная с конца, строка 9; делим получившуюся сумму на 10 и получаем целую часть от деления на десять, которую мы сразу прибавляем к следующему разряду, строка 10. В строке 11, мы отсекаем первый разряд полученного числа, если конечно он есть.
Вот и все. Главное не забыть, что число будет храниться в массиве b и выводить его следует с конца.

Вычитание длинных чисел

Вторая, наиболее используемая арифметическая операция – это вычитание. И эта часть статьи поможет Вам научиться писать алгоритмы вычитания больших и огромных чисел.
Будем считать, что наши числа хранятся в массивах a и b, m и n – длинны этих чисел соответственно. Следует учесть, что числа записаны “зеркально”(см. выше). Конечно, если мы знаем какое число больше, то задача упрощается. Но мы можем и не знать этого. Тогда нам следует сперва найти, какое из чисел больше. Это нам понадобится для определения знака получившегося числа, то есть, если первое число меньше второго, то в ответе появится минус. И так, приступим к написанию первой части алгоритма, то есть определению большего числа. Алгоритм выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int k = 3; // если к == 3, значит числа одинаковой длинны
length = size_a;
if (size_a > size_b)
{
length = size_a;
k = 1; // если к == 1, значит первое число длиннее второго
}
else
if (size_b > size_a)
{
length = size_b;
k = 2; // если к == 2, значит второе число длиннее первого
}
else // если числа одинаковой длинны, то необходимо сравнить их веса
for (int ix = 0; ix < length;) // поразрядное сравнение весов чисел
{
if (a[ix] > b[ix]) // если разряд первого числа больше
{
k = 1; // значит первое число длиннее второго
break; // выход из цикла for
}

if(b[ix] > a[ix]) // если разряд второго числа больше
{
k = 2; // значит второе число длиннее первого
break; // выход из цикла for
}
} // конец for
Теперь поясню написанное. Сначала Вы можете увидеть, что переменной k придается значение 3. В данной части алгоритма переменная k является флагом результата проверки. Если числа равны, то k останется равно 3, если первое больше второго, то k примет значение 1, если второе больше первого, то k примет значение 2. Переменная length примет значение длинны большего числа. Теперь перейдем к обоснованию работоспособности этого алгоритма. Сравнение чисел происходит в два этапа. Сначала мы сравниваем длинны чисел: какое число длиннее, то и больше, строки 1 — 11. Если числа одинаковой длинны, то переходим к по разрядовому сравнению, строки 13 — 26. Начинаем по порядку сравнивать разряды начиная с самого старшего, так мы определим, больший вес числа. В этом и заключается суть и сложность первой части. Теперь перейдем ко второй части алгоритма — вычитание. Она выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int difference (int x, int y, int z, int length)
{
for (int ix = 0; ix < (length - 1); ix++) // проход по всем разрядам числа, начиная с последнего, не доходя до первого
{
if (ix < (length - 1)) // если текущий разряд чисел не первый
{
x[ix + 1]--; // в следующуем разряде большего числа занимаем 1.
z[ix] += 10 + x[ix]; // в ответ записываем сумму значения текущего разряда большего числа и 10-ти

} else // если текущий разряд чисел - первый
z[ix] += x[ix]; // в ответ суммируем значение текущего разряда большего числа

z[ix] -= y[ix]; // вычитаем значение текущего разряда меньшего числа

if (z[ix] / 10 > 0) // если значение в текущем разряде двухразрядное
{
z[ix + 1]++; // переносим единицу в старший разряд
z[ix] %= 10; // в текущем разряде отсекаем ее
}
}
return 0;
}
Для самого вычитания удобно написать функцию, ведь нам тогда не придется писать два алгоритма для двух случаев: когда первое число больше второго, и наоборот. В массиве x мы содержим большее число, в массиве y – меньшее, в массиве z – результат. Алгоритм довольно простой: для каждого разряда мы добавляем 10, с учетом вычитания из старшего разряда — 1. Это делается для упрощения вычитания разрядов. Эта операция делается лишь в том случае, когда рассматриваемый разряд не является последним в массиве (первым в числе). После вычитания разрядов мы проверяем получившееся число в данном разряде в массиве z. Ответ запишется в массив z, причем “зеркальным” (см. выше) способом. Процедуру следует вызывать следующим образом:

1
2
if (k == 1) difference(a,b,c, length); - если первое число больше второго,
if (k == 2) difference(b,a,c, length); - если второе число больше первого.
Теперь ответ будет храниться в массиве c все в том же “зеркальном” порядке. Вот мы и научились вычитать большие и огромные числа.

Умножение длинных чисел

Теперь перейдем к последней, но не менее важной теме нашей статьи – к произведению. Этот алгоритм чаще двух предыдущих можно встретить при решении задач. Собственно меньше демогогики. Перейдем непосредственно к самому алгоритму. Представляю Вашему вниманию алгоритм:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
length = size_a + size_b + 1;

for (int ix = 0; ix < size_a; ix++)
for (int jx = 0; jx < size_b; jx++)
c[ix + jx - 1] += a[ix]
b[ix];

for (int ix = 0; ix < length; ix++)
{
c[ix + 1] += c[ix] / 10;
c[ix] %= 10;
}

while (c[length] == 0)
length-- ;
Вот так выглядит алгоритм задачи. Теперь попробуем “это” разобрать, точнее разобраться, как это работает. Сначала у нас имелось два числа в массивах a и b все в том же “зеркальном” (см. выше) виде. Длинны чисел хранятся в переменных size_a и size_b. В переменной length хранится длинна результирующего числа. Она будет равна либо сумме длин первоначальных чисел, либо этой сумме увеличенной на единицу. Но так, как мы не знаем точной длинны полученного числа, то возьмем длину побольше, то есть второй вариант. Теперь после этих не хитрых подсчетов, приступим к перемножению чисел. Будем их перемножать так, как нас учили в школе. Для этого запускаем два цикла: один до size_a, другой до size_b. После этих циклов вы можете увидеть еще один до length. благодаря ему в записи числа в массиве, в каждой ячейке массива мы получаем по одной цифре
sageАноним 10/05/15 Вск 03:28:01 #206 №92627915 DELETED
Двоичный(бинарный) поиск — алгоритм поиска элемента в отсортированном массиве. Бинарный поиск нашел себе применение в математике и информатике. Возможно, Вы не будете пользоваться алгоритмом двоичного поиска, но знать его принцип работы должны. Двоичный поиск можно использовать только в том случае, если есть массив, все элементы которого упорядочены (отсортированы). Бинарный поиск не используется для поиска максимального или минимального элементов, так как в отсортированном массиве эти элементы содержатся в начале и в конце массива соответственно, в зависимости от тога как отсортирован массив, по возрастанию или по убыванию. Поэтому алгоритм бинарного поиска применим, если необходимо найти некоторый ключевой элемент в массиве. То есть организовать поиск по ключу, где ключ — это определённое значение в массиве. Разработаем программу, в которой объявим одномерный массив, и организуем двоичный поиск.Объявленный массив нужно инициализировать некоторыми значениями, причём так, чтобы эти значения были упорядочены.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// binary_search.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
const int size_array = 10;
int array_[size_array] = {-8, -7, -6, -6, -4, 2, 6, 7, 8, 15 }; // объявление одномерного массива
cout << "array[" << size_array << "] = { ";
for (int counter = 0; counter < size_array; counter++)
{
cout << array_[counter] << " "; // печать элементов одномерного массива array1
}
cout << " }";
int average_index = 0, // переменная для хранения индекса среднего элемента массива
first_index = 0, // индекс первого элемента в массиве
last_index = size_array -1, // индекс последнего элемента в массиве
//--------------------------------------------------------
search_value = 15; // искомое (ключевое) значение
//--------------------------------------------------------
if (last_index == -1) cout << "\narray is empty" << endl; // массив пуст

while (first_index < last_index)
{
average_index = first_index + (last_index - first_index) / 2; // меняем индекс среднего значения
search_value <= array_[average_index] ? last_index = average_index : first_index = average_index + 1; // найден ключевой элемент или нет
}
if ( array_[last_index] == search_value)
cout << "\nvalue is found" << "\nindex = " << last_index << endl;
else
cout << "\nvalue is not found" << endl;
system("pause");
return 0;
}
Итак, в строке 9 объявлена целочисленная переменная константа, которой присвоено значение 10 — размер одномерного массива. Согласно тону хорошего программирования размер статического массива должен задаваться в отдельной переменной, с квалификатором const. В строке 10 объявлен одномерный массив соответствующего размера. Строки 11 — 16 выводят на экран элементы массива с некоторым оформлением. В строках 17 -19 объявляются переменные. которые будут использоваться в алгоритме двоичного поиска В строке 21 объявлена переменная, значение в которой будет искомым. В строка 23 — 33находится алгоритм двоичного поискав массивах. Сначала нужно проверить размер массива, в котором будет искаться ключевое значение, строка 23. Массив может быть нулевого размера, если размер массива больше или равен 1, тогда начинаем искать ключевое значение. Объявление цикла while строки 25 — 29, в цикле организован поиск значения таким образом, что после выхода из цикла индекс найденного значения сохранится в переменной last_index. В телецикла, строка 28 объявлена условная операция ? : , хотя можно было воспользоваться оператором выбора if else. И наконец, в строках 30 — 33 объявлен оператор условного выбора if else, который определяет, говорит о том, было ли найдено искомое значение или нет.



Рассмотрим пошагово, как именно работает алгоритм двоичного поиска в массивах на псевдокоде:
Шаг первый:
проверка условия цикла: 0 < 9 — true
average_index = 0 + (9 — 0) / 2 = 4, значит средний элемент -4
проверка в условной операции 15 <= (-4) — false
значит first_index = 4 + 1 = 5
Шаг второй:
проверка условия цикла: 5 < 9 — true
average_index = 5 + (9 — 5) / 2 = 7, значит средний элемент 7
проверка в условной операции 15 <= 7 — false
значит first_index = 7 + 1 = 8
Шаг третий:
проверка условия цикла: 8 < 9 — true
average_index = 8 + (9 — 8) / 2 = 8 // значит средний элемент 8
проверка в условной операции 15 <= 8 — false
значит first_index = 8 + 1 = 9
Шаг четвёртый:
проверка условия цикла: 9 < 9 — false // выходим из цикла
при этом значение в переменной last_index не менялось, так как искомое значение в последнем элементе массива
Так как, алгоритмы сортировки, нам пока не известны, то массив инициализируем вручную, причём обязательно упорядоченно. В строке 21 указываем искомый элемент массива и запускаем программу (см. Рисунок 2).
Аноним 10/05/15 Вск 03:28:21 #207 №92627926 
>>92627858
Nagi ichi
Abgrund
Hinemosunotari
Incase
Lokon
Все, что тебе нужно знать. Тут даже особо обсуждать и нечего, гуглишь этих художников на exhentai и дрочишь.
sageАноним 10/05/15 Вск 03:28:23 #208 №92627928 DELETED
Пожалуй, самый простой алгоритм сортировок – это сортировка выбором. Судя по названию сортировки, необходимо что-то выбирать (максимальный или минимальный элементы массива). Алгоритм сортировки выбором находит в исходном массиве максимальный или минимальный элементы, в зависимости от того как необходимо сортировать массив, по возрастанию или по убыванию. Если массив должен быть отсортирован по возрастанию, то из исходного массива необходимо выбирать минимальные элементы. Если же массив необходимо отсортировать по убыванию, то выбирать следует максимальные элементы.

Допустим необходимо отсортировать массив по возрастанию. В исходном массиве находим минимальный элемент, меняем его местами с первым элементом массива. Уже, из всех элементов массива один элемент стоит на своём месте. Теперь будем рассматривать не отсортированную часть массива, то есть все элементы массива, кроме первого. В неотсортированной части массива опять ищем минимальный элемент. Найденный минимальный элемент меняем местами со вторым элементом массива и т. д. Таким образом, суть алгоритма сортировки выбором сводится к многократному поиску минимального (максимального) элементов в неотсортированной части массива. Отсортируем массив из семи чисел согласно алгоритму «Сортировка выбором».

исходный массив: 3 3 7 1 2 5 0
1)Итак, находим минимальный элемент в массиве. 0 – минимальный элемент
2)Меняем местами минимальный и первый элементы массива.
Текущий массив: 0 3 7 1 2 5 3
3) Находим минимальный элемент в неотсортированной части массива. 1 – минимальный элемент
4) Меняем местами минимальный и первый элементы массива.
Текущий массив: 0 1 7 3 2 5 3
5) min = 2
6) Текущий массив: 0 1 2 3 7 5 3
7)min = 3
8) Текущий массив: 0 1 2 3 7 5 3 в массиве ничего не поменялось, так как 3 стоит на своём месте
9) min = 3
10) Конечный вид массива: 0 1 2 3 3 5 7 – массив отсортирован

Запрограммируем алгоритм сортировки выбором в С++.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// sorting_choices.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;

void choicesSort(int, int); // прототип функции сортировки выбором

int main(int argc, char
argv[])
{
srand(time(NULL));
setlocale(LC_ALL, "rus");
cout << "Введите размер массива: ";
int size_array; // длинна массива
cin >> size_array;

int sorted_array = new int [size_array]; // одномерный динамический массив
for (int counter = 0; counter < size_array; counter++)
{
sorted_array[counter] = rand() % 100; // заполняем массив случайными числами
cout << setw(2) << sorted_array[counter] << " "; // вывод массива на экран
}
cout << "\n\n";

choicesSort(sorted_array, size_array); // вызов функции сортировки выбором

for (int counter = 0; counter < size_array; counter++)
{
cout << setw(2) << sorted_array[counter] << " "; // печать отсортированного массива
}
cout << "\n";
delete [] sorted_array; // высвобождаем память
system("pause");
return 0;
}

void choicesSort(int
arrayPtr, int length_array) // сортировка выбором
{
for (int repeat_counter = 0; repeat_counter < length_array; repeat_counter++)
{
int temp = arrayPtr[0]; // временная переменная для хранения значения перестановки
for (int element_counter = repeat_counter + 1; element_counter < length_array; element_counter++)
{
if (arrayPtr[repeat_counter] > arrayPtr[element_counter])
{
temp = arrayPtr[repeat_counter];
arrayPtr[repeat_counter] = arrayPtr[element_counter];
arrayPtr[element_counter] = temp;
}
}
}
}
Алгоритм сортировки выбором основан на алгоритме поиска максимального (минимального) элемента. Фактически алгоритм поиска является важнейшей частью сортировки выбором. Так как основная задача сортировки — упорядочивание элементов массива, необходимо выполнять перестановки. Обмен значений элементов сортируемого массива происходит в строках 48 -50. Если поменять знак > в строке 46 на знак меньше, то сортироваться массив будет по убыванию. Результат работы программы показан на рисунке 1.
sageАноним 10/05/15 Вск 03:28:48 #209 №92627949 DELETED
Сортировка вставками — достаточно простой алгоритм. Как в и любом другом алгоритме сортировки, с увеличением размера сортируемого массива увеличивается и время сортировки. Основным преимуществом алгоритма сортировки вставками является возможность сортировать массив по мере его получения.То есть имея часть массива, можно начинать его сортировать. В параллельном программирование такая особенность играет не маловажную роль.

Сортируемый массив можно разделить на две части — отсортированная часть и неотсортированная. В начале сортировки первый элемент массива считается отсортированным, все остальные — не отсортированные. Начиная со второго элемента массива и заканчивая последним, алгоритм вставляет неотсортированный элемент массива в нужную позицию в отсортированной части массива. Таким образом, за один шаг сортировки отсортированная часть массива увеличивается на один элемент, а неотсортированная часть массива уменьшается на один элемент. Рассмотрим пример сортировки по возрастанию массива из 7 чисел (см. Таблица 1):
исходный массив: 3 3 7 1 2 5 0

Таблица 1 — Сортировка вставками
шаг отсортированная часть массива тек. элемент вставка
1 3 3 false
2 3 3 7 false
3 3 3 7 1 true
4 1 3 3 7 2 true
5 1 2 3 3 7 5 true
6 1 2 3 3 5 7 0 true
- 0 1 2 3 3 5 7 - -
На каждом шаге сортировки сравнивается текущий элемент со всеми элементами в отсортированной части. На первом шаге сравнивается тройка с тройкой, они равны поэтому не меняем их местами. На втором шаге сравниваем 7 с двумя тройками, 7 > 3 а так как сортировка по возрастанию, то опять элементы массива остаются на своих местах. На третьем шаге единица сравнивается с тремя элементами и все они больше единицы, значит единицу вставляем на первое место, в начало массива. На четвёртом шаге текущий элемент — 2 сравниваем с элементами 1, 3, 3, 7. Получается, что 1 < 2 < 3 и 7 поэтому двойку вставляем между единицей и тройкой. Пятый и шестой шаги выполняются точно также. В итоге на шестом шагу мы получаем отсортированный по возрастанию массив. Запрограммируем алгоритм сортировки вставками на С++.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// insertion_sort.cpp: определяет точку входа для консольного приложения.

#include "stdafx.h"
#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;

void insertionSort(int , int); // прототип функции сортировки вставками

int main(int argc, char
argv[])
{
srand(time(NULL));
setlocale(LC_ALL, "rus");
cout << "Введите размер массива: ";
int size_array; // длинна массива
cin >> size_array;

int sorted_array = new int [size_array]; // одномерный динамический массив
for (int counter = 0; counter < size_array; counter++)
{
sorted_array[counter] = rand() % 100; // заполняем массив случайными числами
cout << setw(2) << sorted_array[counter] << " "; // вывод массива на экран
}
cout << "n";

insertionSort(sorted_array, size_array); // вызов функции сортировки вставками

for (int counter = 0; counter < size_array; counter++)
{
cout << setw(2) << sorted_array[counter] << " "; // печать отсортированного массива
}
cout << "n";
delete [] sorted_array; // высвобождаем память
system("pause");
return 0;
}

void insertionSort(int
arrayPtr, int length) // сортировка вставками
{
int temp, // временная переменная для хранения значения элемента сортируемого массива
item; // индекс предыдущего элемента
for (int counter = 1; counter < length; counter++)
{
temp = arrayPtr[counter]; // инициализируем временную переменную текущим значением элемента массива
item = counter-1; // запоминаем индекс предыдущего элемента массива
while(item >= 0 && arrayPtr[item] > temp) // пока индекс не равен 0 и предыдущий элемент массива больше текущего
{
arrayPtr[item + 1] = arrayPtr[item]; // перестановка элементов массива
arrayPtr[item] = temp;
item--;
}
}
}
Программа сортирует одномерный массив по возрастанию. Изменив знак > в строке 47 массив будет сортироваться по убыванию. Результат работы программы показан на рисунке 1.
sageАноним 10/05/15 Вск 03:30:08 #210 №92628008 DELETED
Существуют, конечно, программы еще большего размера. Однако те из них,
которые действительно используются, обычно можно разбить на несколько
практически независимых частей, каждая из которых имеет значительно
меньший упомянутого размер. Естественно, трудность написания и
сопровождения программы определяется не только числом строк текста, но и
сложностью предметной области. Так что приведенные здесь числа, которыми
обосновывались наши соображения, не надо воспринимать слишком серьезно.

К сожалению, не всякую часть программы можно хорошо структурировать,
сделать независимой от аппаратуры, достаточно понятной и т.д. В С++ есть
средства, непосредственно и эффективно представляющие аппаратные
возможности. Их использование позволяет избавиться от беспокойства о
надежности и простоте понимания программы. Такие части программы можно
скрывать, предоставляя надежный и простой интерфейс с ними.

Естественно, если С++ используется для большой программы, то это
означает, что язык используют группы программистов. Полезную роль здесь
сыграют свойственные языку модульность, гибкость и строго типизированные
интерфейсы. В С++ есть такой же хороший набор средств для создания больших
программ, как во многих языках. Но когда программа становится еще больше,
проблемы по ее созданию и сопровождению перемещаются из области языка в
более глобальную область программных средств и управления проектом. Этим
вопросам посвящены главы 11 и 12.

В этой книге основное внимание уделяется методам создания
универсальных средств, полезных типов, библиотек и т.д. Эти методы можно
успешно применять как для маленьких, так и для больших программ. Более
того, поскольку все нетривиальные программы состоят из нескольких в
значительной степени независимых друг от друга частей, методы
программирования отдельных частей пригодятся как системным, так и
прикладным программистам.

Может возникнуть подозрение, что запись программы с использованием
подробной системы типов, увеличит размер текста. Для программы на С++ это
не так: программа на С++, в которой описаны типы формальных параметров
функций, определены классы и т.п., обычно бывает даже короче своего
эквивалента на С, где эти средства не используются. Когда в программе на
С++ используются библиотеки, она также оказывается короче своего
эквивалента на С, если, конечно, он существует.


Философские замечания

Язык программирования решает две взаимосвязанные задачи: позволяет
программисту записать подлежащие выполнению действия и формирует понятия,
которыми программист оперирует, размышляя о своей задаче. Первой цели
идеально отвечает язык, который очень "близок машине". Тогда со всеми ее
основными "сущностями" можно просто и эффективно работать на этом языке,
причем делая это очевидным для программиста способом. Именно это имели в
виду создатели С. Второй цели идеально отвечает язык, который настолько
"близок к поставленной задаче", что на нем непосредственно и точно
выражаются понятия, используемые в решении задачи. Именно это имелось в
виду, когда первоначально определялись средства, добавляемые к С.

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

Типизация языка должна быть особенно полезна для нетривиальных задач.
Действительно, понятие класса в С++ проявило себя как мощное
концептуальное средство.


Замечания о программировании на языке С++

Предполагается, что в идеальном случае разработка программы делится на
три этапа: вначале необходимо добиться ясного понимания задачи, затем
определить ключевые понятия, используемые для ее решения, и, наконец,
полученное решение выразить в виде программы. Однако, детали решения и
точные понятия, которые будут использоваться в нем, часто проясняются
только после того, как их попытаются выразить в программе. Именно в этом
случае большое значение приобретает выбор языка программирования.

Во многих задачах используются понятия, которые трудно представить в
программе в виде одного из основных типов или в виде функции без связанных
с ней статических данных. Такое понятие может представлять в программе
класс. Класс - это тип; он определяет поведение связанных с ним объектов:
их создание, обработку и уничтожение. Кроме этого, класс определяет
реализацию объектов в языке, но на начальных стадиях разработки программы
это не является и не должно являться главной заботой. Для написания
хорошей программы надо составить такой набор классов, в котором каждый
класс четко представляет одно понятие. Обычно это означает, что
программист должен сосредоточиться на вопросах: Как создаются объекты
данного класса? Могут ли они копироваться и (или) уничтожаться? Какие
операции можно определить над этими объектами? Если на эти вопросы
удовлетворительных ответов не находится, то, скорее всего, это означает,
что понятие не было достаточно ясно сформулировано. Тогда, возможно,
стоит еще поразмышлять над задачей и предлагаемым решением, а не
немедленно приступать к программированию, надеясь в процессе него найти
ответы.

Проще всего работать с понятиями, которые имеют традиционную
математическую форму представления: всевозможные числа, множества,
геометрические фигуры и т.д. Для таких понятий полезно было бы иметь
стандартные библиотеки классов, но к моменту написания книги их еще не
было. В программном мире накоплено удивительное богатство из таких
библиотек, но нет ни формального, ни фактического стандарта на них. Язык
С++ еще достаточно молод, и его библиотеки не развились в такой степени,
как сам язык.

Понятие не существует в вакууме, вокруг него всегда группируются
связанные с ним понятия. Определить в программе взаимоотношения классов,
иными словами, установить точные связи между используемыми в задаче
понятиями, бывает труднее, чем определить каждый из классов сам по себе. В
результате не должно получиться "каши" - когда каждый класс (понятие)
зависит от всех остальных. Пусть есть два класса A и B. Тогда связи между
ними типа "A вызывает функцию из B", "A создает объекты B", "A имеет член
типа B" обычно не вызывают каких-либо трудностей. Связи же типа "A
использует данные из B", как правило, можно вообще исключить.

Одно из самых мощных интеллектуальных средств, позволяющих справиться
со сложностью, - это иерархическое упорядочение, т.е. упорядочение
связанных между собой понятий в древовидную структуру, в которой самое
общее понятие находится в корне дерева. Часто удается организовать классы
программы как множество деревьев или как направленный ацикличный граф. Это
означает, что программист определяет набор базовых классов, каждый из
которых имеет свое множество производных классов. Набор операций самого
общего вида для базовых классов (понятий) обычно определяется с помощью
виртуальных функций ($$6.5). Интерпретация этих операций, по мере
надобности, может уточняться для каждого конкретного случая, т.е. для
каждого производного класса.

Естественно, есть ограничения и при такой организации программы.
Иногда используемые в программе понятия не удается упорядочить даже с
помощью направленного ацикличного графа. Некоторые понятия оказываются по
своей природе взаимосвязанными. Циклические зависимости не вызовут
проблем, если множество взаимосвязанных классов настолько мало, что в нем
легко разобраться. Для представления на С++ множества взаимозависимых
sageАноним 10/05/15 Вск 03:30:29 #211 №92628020 DELETED
Чем лучше программист знает С, тем труднее будет для него при
программировании на С++ отойти от стиля программирования на С. Так он
теряет потенциальные преимущества С++. Поэтому советуем просмотреть раздел
"Отличия от С" в справочном руководстве ($$R.18). Здесь мы только укажем
на те места, в которых использование дополнительных возможностей С++
приводит к лучшему решению, чем программирование на чистом С. Макрокоманды
практически не нужны в С++: используйте const ($$2.5) или enum ($$2.5.1),
чтобы определить поименованные константы; используйте inline ($$4.6.2),
чтобы избежать расходов ресурсов, связанных с вызовом функций; используйте
шаблоны типа ($$8), чтобы задать семейство функций и типов. Не описывайте
переменную, пока она действительно вам не понадобится, а тогда ее можно
сразу инициализировать, ведь в С++ описание может появляться в любом
месте, где допустим оператор. Не используйте malloc(), эту операцию лучше
реализует new ($$3.2.6). Объединения нужны не столь часто, как в С,
поскольку альтернативность в структурах реализуется с помощью производных
классов. Старайтесь обойтись без объединений, но если они все-таки нужны,
не включайте их в основные интерфейсы; используйте безымянные объединения
($$2.6.2). Старайтесь не использовать указателей типа void*,
арифметических операций с указателями, массивов в стиле С и операций
приведения. Если все-таки вы используете эти конструкции, упрятывайте их
достаточно надежно в какую-нибудь функцию или класс. Укажем, что
связывание в стиле С возможно для функции на С++, если она описана со
спецификацией extern "C" ($$4.4).

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


Список литературы

В книге немного непосредственных ссылок на литературу. Здесь приведен
список книг и статей, на которые есть прямые ссылки, а также тех, которые
только упоминаются.

[1] A.V.Aho, J.E.Hopcroft, and J.D.Ulman: Data Structures and
Algoritms. Addison-Wesley, Reading, Massachusetts. 1983.

[2] O-J.Dahl, B.Myrhaug, and K.Nugaard: SIMULA Common Base Language.
Norwegian Computing Ctnter S-22. Oslo, Norway. 1970

[3] O-J.Dahl and C.A.R.Hoare: Hierarhical Program Construction in
Structured Programming. Academic Press, New York. 1972. pp. 174-220.

[4] Margaret A.Ellis and Bjarne Stroustrup: The Annotated C++
Reference Manual. Addison-Wesley, Reading, Massachusetts. 1990.

[5] A.Goldberg and D.Rodson: SMALLTALK-80 - The Language and Its
Implementation. Addison-Wesley, Reading, Massachusetts. 1983.

[6] R.E.Griswold et.al.: The Snobol14 Programming Language.
Prentice-Hall, Englewood Cliffs, New Jersy, 1970.

[7] R.E.Griswold and M.T.Griswold: The ICON Programming Language.
Prentice-Hall, Englewood Cliffs, New Jersy. 1983.

[8] Brian W.Kernighan and Dennis M.Ritchie: The C Programming
Language. Prentice-Hall, Englewood Cliffs, New Jersy. 1978. Second
edition 1988.

[9] Andrew Koenig and Bjarne Stroustrup: C++: As Close to C as
possible - but no closer. The C++ Report. Vol.1 No.7. July 1989.

[10] Andrew Koenig and Bjarne Stroustrup: Exception Handling for C++
(revised). Proc USENIX C++ Conference, April 1990. Also, Journal of Object
Oriented Programming, Vol.3 No.2, July/August 1990. pp.16-33.

[11] Barbara Liskov et.al.: CLU Reference Manual. MIT/LCS/TR-225.

[12] George Orwell: 1984. Secker and Warburg, London. 1949.

[13] Martin Richards and Colin Whitby-Strevens: BCPL - The Language
and Its Compiler. Cambridge University Press. 1980.

[14] L.Rosler: The Evolution of C - Past and Future. AT&T Bell
Laboratories Technical Journal. Vol.63 No.8 Part 2. October 1984.
pp.1685-1700.

[15] Ravi Sethi: Uniform Syntax for Type Expressions and Declarations.
Software Practice & Experience, Vol.11. 1981. pp.623-628.

[16] Bjarne Stroustrup: Adding Classes to C: An Exercise in Language
Evolution. Software Practice & Experience, Vol.13. 1983. pp.139-61.

[17] Bjarne Stroustrup: The C++ Programming Language. Addison-Wesley.
1986.

[18] Bjarne Stroustrup: Multiple Inheritance for C++. Proc. EUUG
Spring Conference, May 1987. Also USENIX Computer Systems, Vol.2 No 4,
Fall 1989.

[19] Bjarne Stroustrup and Jonathan Shopiro: A Set of C classes for
Co-Routine Style Programming. Proc. USENIX C++ conference, Santa Fe.
November 1987. pp.417-439.
Аноним 10/05/15 Вск 03:30:34 #212 №92628027 
>>92627889
Я же не буду весь вечер сидеть на нулевой. Не выйдет.

>>92627926
Да я оглядел тред, схоронил как раз из-за этих ссылок.
sageАноним 10/05/15 Вск 03:30:51 #213 №92628042 DELETED
Конечно такое решение намного лучше, чем хаос, свойственный
традиционным, неструктурированным решениям, но моделируемые таким способом
типы совершенно очевидно отличаются от "настоящих", встроенных. Каждый
управляющий типом модуль должен определять свой собственный алгоритм
создания "переменных" этого типа. Не существует универсальных правил
присваивания идентификаторов, обозначающих объекты такого типа. У
"переменных" таких типов не существует имен, которые были бы известны
транслятору или другим системным программам, и эти "переменные" не
подчиняются обычным правилам областей видимости и передачи параметров.

Тип, реализуемый управляющим им модулем, по многим важным аспектам
существенно отличается от встроенных типов. Такие типы не получают той
поддержки со стороны транслятора (разного вида контроль), которая
обеспечивается для встроенных типов. Проблема здесь в том, что программа
формулируется в терминах небольших (одно-два слова) дескрипторов объектов,
а не в терминах самих объектов ( stack_id может служить примером такого
дескриптора). Это означает, что транслятор не сможет отловить глупые,
очевидные ошибки, вроде тех, что допущены в приведенной ниже функции:

void f ()
{
stack_id s1;
stack_id s2;

s1 = create_stack ( 200 );
// ошибка: забыли создать s2

push ( s1,'a' );
char c1 = pop ( s1 );

destroy_stack ( s2 ); // неприятная ошибка

// ошибка: забыли уничтожить s1

s1 = s2; // это присваивание является по сути
// присваиванием указателей,
// но здесь s2 используется после уничтожения
}

Иными словами, концепция модульности, поддерживающая парадигму
упрятывания данных, не запрещает такой стиль программирования, но и не
способствует ему.

В языках Ада, Clu, С++ и подобных им эта трудность преодолевается
благодаря тому, что пользователю разрешается определять свои типы, которые
трактуются в языке практически так же, как встроенные. Такие типы обычно
называют абстрактными типами данных, хотя лучше, пожалуй, их называть
просто пользовательскими. Более строгим определением абстрактных типов
данных было бы их математическое определение. Если бы удалось его дать,
то, что мы называем в программировании типами, было бы конкретным
представлением действительно абстрактных сущностей. Как определить "более
абстрактные" типы, показано в $$4.6. Парадигму же программирования можно
выразить теперь так:
sageАноним 10/05/15 Вск 03:31:08 #214 №92628055 DELETED
Определите, какие типы вам нужны; предоставьте полный набор операций
для каждого типа.

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

Арифметические типы, подобные типам рациональных и комплексных чисел,
являются типичными примерами пользовательских типов:

class complex
{
double re, im;
public:
complex(double r, double i) { re=r; im=i; }
complex(double r) // преобразование float->complex
{ re=r; im=0; }
friend complex operator+(complex, complex);
friend complex operator-(complex, complex); // вычитание
friend complex operator-(complex) // унарный минус
friend complex operator(complex, complex);
friend complex operator/(complex, complex);
// ...
};

Описание класса (т.е. определяемого пользователем типа) complex задает
представление комплексного числа и набор операций с комплексными числами.
Представление является частным (private): re и im доступны только для
функций, указанных в описании класса complex. Подобные функции могут быть
определены так:

complex operator + ( complex a1, complex a2 )
{
return complex ( a1.re + a2.re, a1.im + a2.im );
}

и использоваться следующим образом:

void f ()
{
complex a = 2.3;
complex b = 1 / a;
complex c = a + b
complex ( 1, 2.3 );
// ...
c = - ( a / b ) + 2;
}

Большинство модулей (хотя и не все) лучше определять как
пользовательские типы.


1.2.4 Пределы абстракции данных



Абстрактный тип данных определяется как некий "черный ящик". После
своего определения он по сути никак не взаимодействует с программой. Его
никак нельзя приспособить для новых целей, не меняя определения. В этом
смысле это негибкое решение. Пусть, например, нужно определить для
графической системы тип shape (фигура). Пока считаем, что в системе могут
быть такие фигуры: окружность (circle), треугольник (triangle) и квадрат
(square). Пусть уже есть определения точки и цвета:

class point { / ... / };
class color { / ... / };

Тип shape можно определить следующим образом:

enum kind { circle, triangle, square };

class shape
{
point center;
color col;
sageАноним 10/05/15 Вск 03:31:30 #215 №92628074 DELETED
kind k;
// представление фигуры
public:
point where () { return center; }
void move ( point to ) { center = to; draw (); }
void draw ();
void rotate ( int );
// еще некоторые операции
};

"Поле типа" k необходимо для того, чтобы такие операции, как draw () и
rotate (), могли определять, с какой фигурой они имеют дело (в языках
вроде Паскаля можно использовать для этого запись с вариантами, в которой
k является полем-дескриминантом). Функцию draw () можно определить так:

void shape :: draw ()
{

switch ( k )
{
case circle:
// рисование окружности
break;
case triangle:
// рисование треугольника
break;
case square:
// рисование квадрата
break;
}
}

Это не функция, а кошмар. В ней нужно учесть все возможные фигуры,
какие только есть. Поэтому она дополняется новыми операторами, как только
в системе появляется новая фигура. Плохо то, что после определения новой
фигуры нужно проверить и, возможно, изменить все старые операции класса.
Поэтому, если вам недоступен исходный текст каждой операции класса, ввести
новую фигуру в систему просто невозможно. Появление любой новой фигуры
приводит к манипуляциям с текстом каждой существенной операции класса.
Требуется достаточно высокая квалификация, чтобы справиться с этой
задачей, но все равно могут появиться ошибки в уже отлаженных частях
программы, работающих со старыми фигурами. Возможность выбора
представления для конкретной фигуры сильно сужается, если требовать, чтобы
все ее представления укладывались в уже заданный формат, специфицированный
общим определением фигуры (т.е. определением типа shape).


1.2.5 Объектно-ориентированное программирование



Проблема состоит в том, что мы не различаем общие свойства фигур
(например, фигура имеет цвет, ее можно нарисовать и т.д.) и свойства
конкретной фигуры (например, окружность - это такая фигура, которая имеет
радиус, она изображается с помощью функции, рисующей дуги и т.д.). Суть
объектно-ориентированного программирования в том, что оно позволяет
выражать эти различия и использует их. Язык, который имеет конструкции для
выражения и использования подобных различий, поддерживает
объектно-ориентированное программирование. Все другие языки не
поддерживают его. Здесь основную роль играет механизм наследования,
заимствованный из языка Симула. Вначале определим класс, задающий общие
свойства всех фигур:

class shape
{
point center;
color col;
// ...
public:
point where () { return center; }
void move ( point to ) { center = to; draw(); }
virtual void draw ();
virtual void rotate ( int );
// ...
};

Те функции, для которых можно определить заявленный интерфейс, но
реализация которых (т.е. тело с операторной частью) возможна только для
конкретных фигур, отмечены служебным словом virtual (виртуальные). В
Симуле и С++ виртуальность функции означает: "функция может быть
определена позднее в классе, производном от данного". С учетом такого
определения класса можно написать общие функции, работающие с фигурами:
sageАноним 10/05/15 Вск 03:32:14 #216 №92628106 DELETED
void rotate_all ( shape v [], int size, int angle )
// повернуть все элементы массива "v" размера "size"
// на угол равный "angle"
{
int i = 0;
while ( i<size )
{
v [ i ] . rotate ( angle );
i = i + 1;
}
}

Для определения конкретной фигуры следует указать, прежде всего, что
это - именно фигура и задать ее особые свойства (включая и виртуальные
функции):

class circle : public shape
{
int radius;
public:
void draw () { / ... / };
void rotate ( int ) {} // да, пока пустая функция
};

В языке С++ класс circle называется производным по отношению к классу
shape, а класс shape называется базовым для класса circle. Возможна
другая терминология, использующая названия "подкласс" и "суперкласс" для
классов circle и shape соответственно. Теперь парадигма программирования
формулируется так:

Определите, какой класс вам необходим; предоставьте полный набор
операций для каждого класса; общность классов выразите явно с помощью
наследования.

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

Нахождение общности среди отдельных типов системы представляет собой
нетривиальный процесс. Степень такой общности зависит от способа
проектирования системы. В процессе проектирования выявление общности
классов должно быть постоянной целью. Она достигается двумя способами:
либо проектированием специальных классов, используемых как "кирпичи" при
построении других, либо поиском похожих классов для выделения их общей
части в один базовый класс.
sageАноним 10/05/15 Вск 03:32:31 #217 №92628116 DELETED
Минимальная поддержка процедурного программирования включает функции,
арифметические операции, выбирающие операторы и циклы. Помимо этого должны
быть предоставлены операции ввода- вывода. Базовые языковые средства С++
унаследовал от С (включая указатели), а операции ввода-вывода
предоставляются библиотекой. Самая зачаточная концепция модульности
реализуется с помощью механизма раздельной трансляции.


1.3.1 Программа и стандартный вывод



Самая маленькая программа на С++ выглядит так:

main () { }

В этой программе определяется функция, называемая main, которая не
имеет параметров и ничего не делает. Фигурные скобки { и } используются в
С++ для группирования операторов. В данном случае они обозначают начало и
конец тела (пустого) функции main. В каждой программе на С++ должна быть
своя функция main(), и программа начинается с выполнения этой функции.

Обычно программа выдает какие-то результаты. Вот программа, которая
выдает приветствие Hello, World! (Всем привет!):

#include <iostream.h>

int main ()
{
cout << "Hello, World!\n";
}

Строка #include <iostream.h> сообщает транслятору, что надо включить в
программу описания, необходимые для работы стандартных потоков ввода-
вывода, которые находятся в iostream.h. Без этих описаний выражение

cout << "Hello, World!\n"

не имело бы смысла. Операция << ("выдать") записывает свой второй
параметр в первый параметр. В данном случае строка "Hello, World!\n"
записывается в стандартный выходной поток cout. Строка - это
последовательность символов, заключенная в двойные кавычки. Два символа:
обратной дробной черты \ и непосредственно следующий за ним - обозначают
некоторый специальный символ. В данном случае \n является символом конца
строки (или перевода строки), поэтому он выдается после символов Hello,
world!

Целое значение, возвращаемое функцией main(), если только оно есть,
считается возвращаемым системе значением программы. Если ничего не
возвращается, система получит какое-то "мусорное" значение.

Средства ввода/вывода потоковой библиотеки подробно описываются в
главе 10.


1.3.2 Переменные и арифметические операции



Каждое имя и каждое выражение обязаны иметь тип. Именно тип определяет
операции, которые могут выполняться над ними. Например, в описании

int inch;

говорится, что inch имеет тип int, т.е. inch является целой
переменной.

Описание - это оператор, который вводит имя в программу. В описании
указывается тип имени. Тип, в свою очередь, определяет как правильно
использовать имя или выражение.

Основные типы, наиболее приближенные к "аппаратной реальности" машины,
таковы:

char
short
int
long

Они представляют целые числа. Следующие типы:

float
double
long double
sageАноним 10/05/15 Вск 03:32:47 #218 №92628129 DELETED
редставляют числа с плавающей точкой. Переменная типа char имеет
размер, нужный для хранения одного символа на данной машине (обычно это
один байт). Переменная int имеет размер, необходимый для целой арифметики
на данной машине (обычно это одно слово).

Следующие арифметические операции можно использовать над любым
сочетанием перечисленных типов:

+ (плюс, унарный и бинарный)
- (минус, унарный и бинарный)
(умножение)
/ (деление)
% (остаток от деления)

То же верно для операций отношения:

== (равно)
!= (не равно)
< (меньше чем)
<= (меньше или равно)
>= (больше или равно)

Для операций присваивания и арифметических операций в С++ выполняются
все осмысленные преобразования основных типов, чтобы их можно было
неограниченно использовать любые их сочетания:

double d;
int i;
short s;
// ...
d = d + i;
i = s
i;

Символ = обозначает обычное присваивание.


1.3.3 Указатели и массивы



Массив можно описать так:

char v [ 10 ]; // массив из 10 символов

Описание указателя имеет такой вид:

char p; // указатель на символ

Здесь [] означает "массив из", а символ
означает "указатель на".
Значение нижней границы индекса для всех массивов равно нулю, поэтому v
имеет 10 элементов: v [ 0 ] ... v [ 9 ]. Переменная типа указатель может
содержать адрес объекта соответствующего типа:

p = & v [ 3 ]; // p указывает на 4-й элемент массива v

Унарная операция & означает взятие адреса.


1.3.4 Условные операторы и циклы



В С++ есть традиционный набор выбирающих операторов и циклов. Ниже
приводятся примеры операторов if, switch и while.

В следующем примере показано преобразование дюйма в сантиметр и
обратно. Предполагается, что во входном потоке значение в сантиметрах
завершается символом i, а значение в дюймах - символом c:

#include <iostream.h>

int main ()
{
sageАноним 10/05/15 Вск 03:33:07 #219 №92628143 DELETED
const float fac = 2.54;
float x, in, cm;
char ch = 0;

cout << "enter length: ";

cin >> x; // ввод числа с плавающей точкой
cin >> ch // ввод завершающего символа

if ( ch == 'i' )
{ // дюйм
in = x;
cm = x fac;
}
else if ( ch == 'c' )
{ // сантиметры
in = x / fac;
cm = x;
}
else
in = cm = 0;

cout << in << " in = " << cm << " cm\n";
}

Операция >> ("ввести из") используется как оператор ввода; cin
является стандартным входным потоком. Тип операнда, расположенного справа
от операции >>, определяет, какое значение вводится; оно записывается в
этот операнд.

Оператор switch (переключатель) сравнивает значение с набором
констант. Проверку в предыдущем примере можно записать так:

switch ( ch )
{
case 'i':
in = x;
cm = x
fac;
break;
case 'c':
in = x / fac;
cm = x;
break;
default:
in = cm = 0;
break;
}

Операторы break используются для выхода из переключателя. Все
константы вариантов должны быть различны. Если сравниваемое значение не
совпадает ни с одной из них, выполняется оператор с меткой default.
Вариант default может и отсутствовать.

Приведем запись, задающую копирование 10 элементов одного массива в
другой:

int v1 [ 10 ];
int v2 [ 10 ];
// ...
for ( int i=0; i<10; i++ ) v1 [ i ] = v2 [ i ];

Словами это можно выразить так: "Начать с i равного нулю, и пока i
меньше 10, копировать i-тый элемент и увеличивать i." Инкремент (++)
переменной целого типа просто сводится к увеличению на 1.


1.3.5 Функции



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

extern float pow ( float, int );
// pow () определена в другом месте

int main ()
{
for ( int i=0; i<10; i++ ) cout << pow ( 2, i ) << '\n';
}

Первая строка является описанием функции. Она задает pow как функцию с
параметрами типа float и int, возвращающую значение типа float. Описание
функции необходимо для ее вызова, ее определение находится в другом месте.
sageАноним 10/05/15 Вск 03:33:24 #220 №92628158 DELETED
При вызове функции тип каждого фактического параметра сверяется с
типом, указанным в описании функции, точно так же, как если бы
инициализировалась переменная описанного типа. Это гарантирует надлежащую
проверку и преобразования типов. Например, вызов функции pow(12.3,"abcd")
транслятор сочтет ошибочным, поскольку "abcd" является строкой, а не
параметром типа int. В вызове pow(2,i) транслятор преобразует целую
константу (целое 2) в число с плавающей точкой (float), как того требует
функция. Функция pow может быть определена следующим образом:

float pow ( float x, int n )
{
if ( n < 0 )
error ( "ошибка: для pow () задан отрицательный показатель");
switch ( n )
{
case 0: return 1;
case 1: return x;
default: return x pow ( x, n-1 );
}
}

Первая часть определения функции задает ее имя, тип возвращаемого
значения (если оно есть), а также типы и имена формальных параметров (если
они существуют). Значение возвращается из функции с помощью оператора
return.

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

int pow ( int, int );
double pow ( double, double );
//...
x = pow ( 2,10 ); // вызов pow ( int, int )
y = pow ( 2.0, 10.0 );// вызов pow ( double, double )

Такое многократное использование имени называется перегрузкой имени
функции или просто перегрузкой; перегрузка рассматривается особо в главе
7.

Параметры функции могут передаваться либо "по значению", либо "по
ссылке". Рассмотрим определение функции, которая осуществляет взаимообмен
значений двух целых переменных. Если используется стандартный способ
передачи параметров по значению, то придется передавать указатели:

void swap ( int
p, int q )
{
int t =
p;
p = q;
q = t;
}

Унарная операция
называется косвенностью (или операцией
разыменования), она выбирает значение объекта, на который настроен
указатель. Функцию можно вызывать следующим образом:

void f ( int i, int j )
{
swap ( & i, & j );
}

Если использовать передачу параметра по ссылке, можно обойтись без
явных операций с указателем:

void swap (int & r1, int & r2 )
{
int t = r1;
r1 = r2;
r2 = t;
}

void g ( int i, int j )
{
swap ( i, j );
}

Для любого типа T запись T& означает "ссылка на T". Ссылка служит
синонимом той переменной, которой она инициализировалась. Отметим, что
перегрузка допускает сосуществование двух функций swap в одной программе.


1.3.6 Модули



Программа С++ почти всегда состоит из нескольких раздельно
транслируемых "модулей". Каждый "модуль" обычно называется исходным
файлом, но иногда - единицей трансляции. Он состоит из последовательности
описаний типов, функций, переменных и констант. Описание extern позволяет
из одного исходного файла ссылаться на функцию или объект, определенные в
другом исходном файле. Например:

extern "C" double sqrt ( double );
extern ostream cout;

Самый распространенный способ обеспечить согласованность описаний
внешних во всех исходных файлах - поместить такие описания в специальные
файлы, называемые заголовочными. Заголовочные файлы можно включать во все
исходные файлы, в которых требуются описания внешних. Например, описание
функции sqrt хранится в заголовочном файле стандартных математических
функций с именем math.h, поэтому, если нужно извлечь квадратный корень из
4, можно написать:
sageАноним 10/05/15 Вск 03:33:40 #221 №92628169 DELETED
#include <math.h>
//...
x = sqrt ( 4 );

Поскольку стандартные заголовочные файлы могут включаться во многие
исходные файлы, в них нет описаний, дублирование которых могло бы вызвать
ошибки. Так, тело функции присутствует в таких файлах, если только это
функция-подстановка, а инициализаторы указаны только для констант ($$4.3).
Не считая таких случаев, заголовочный файл обычно служит хранилищем для
типов, он предоставляет интерфейс между раздельно транслируемыми частями
программы.

В команде включения заключенное в угловые скобки имя файла (в нашем
примере - <math.h>) ссылается на файл, находящийся в стандартном каталоге
включаемых файлов. Часто это - каталог /usr/include/CC. Файлы, находящиеся
в других каталогах, обозначаются своими путевыми именами, взятыми в
кавычки. Поэтому в следующих командах:

#include "math1.h"
#include "/usr/bs/math2.h"

включаются файл math1.h из текущего каталога пользователя и файл
math2.h из каталога /usr/bs.

Приведем небольшой законченный пример, в котором строка определяется в
одном файле, а печатается в другом. В файле header.h определяются нужные
типы:

// header.h

extern char prog_name;
extern void f ();

Файл main.c является основной программой:

// main.c

#include "header.h"
char
prog_name = "примитивный, но законченный пример";
int main ()
{
f ();
}

а строка печатается функцией из файла f.c:

// f.c

#include <stream.h>
#include "header.h"
void f ()
{
cout << prog_name << '\n';
}

При запуске транслятора С++ и передаче ему необходимых
файлов-параметров в различных реализациях могут использоваться разные
расширения имен для программ на С++. На машине автора трансляция и запуск
программы выглядит так:

$ CC main.c f.c -o silly
$ silly

примитивный, но законченный пример

$

Кроме раздельной трансляции концепцию модульности в С++ поддерживают
классы ($$5.4).


1.4 Поддержка абстракции данных



Поддержка программирования с абстракцией данных в основном сводится к
возможности определить набор операций (функции и операции) над типом. Все
обращения к объектам этого типа ограничиваются операциями из заданного
набора. Однако, имея такие возможности, программист скоро обнаруживает,
что для удобства определения и использования новых типов нужны еще
некоторые расширения языка. Хорошим примером такого расширения является
перегрузка операций.
sageАноним 10/05/15 Вск 03:33:57 #222 №92628183 DELETED
Инициализация и удаление



Когда представление типа скрыто, необходимо дать пользователю средства
для инициализации переменных этого типа. Простейшее решение - до
использования переменной вызывать некоторую функцию для ее инициализации.
Например:

class vector
{
// ...
public:
void init ( init size ); // вызов init () перед первым
// использованием объекта vector
// ...
};

void f ()
{
vector v;
// пока v нельзя использовать
v.init ( 10 );
// теперь можно
}

Но это некрасивое и чреватое ошибками решение. Будет лучше, если
создатель типа определит для инициализации переменных некоторую
специальную функцию. Если такая функция есть, то две независимые операции
размещения и инициализации переменной совмещаются в одной (иногда ее
называют инсталляцией или просто построением). Функция инициализации
называется конструктором. Конструктор выделяется среди всех прочих функций
данного класса тем, что имеет такое же имя, как и сам класс. Если объекты
некоторого типа строятся нетривиально, то нужна еще одна дополнительная
операция для удаления их после последнего использования. Функция удаления
в С++ называется деструктором. Деструктор имеет то же имя, что и его
класс, но перед ним стоит символ ~ (в С++ этот символ используется для
операции дополнения). Приведем пример:

class vector
{
int sz; // число элементов
int * v; // указатель на целые
public:
vector ( int ); // конструктор
~vector (); // деструктор
int& operator [] ( int index ); // операция индексации
};

Конструктор класса vector можно использовать для контроля над ошибками
и выделения памяти:

vector::vector ( int s )
{
if ( s <= 0 )
error ( "недопустимый размер вектора" );
sz = s;
v = new int [ s ]; // разместить массив из s целых
}

Деструктор класса vector освобождает использовавшуюся память:

vector::~vector ()
{
delete [] v; // освободить массив, на который
// настроен указатель v
}

От реализации С++ не требуется освобождения выделенной с помощью new
памяти, если на нее больше не ссылается ни один указатель (иными словами,
не требуется автоматическая "сборка мусора"). В замен этого можно без
вмешательства пользователя определить в классе собственные функции
управления памятью. Это типичный способ применения конструкторов и
деструкторов, хотя есть много не связанных с управлением памятью
применений этих функций (см., например, $$9.4).
sageАноним 10/05/15 Вск 03:34:14 #223 №92628195 DELETED
Присваивание и инициализация



Для многих типов задача управления ими сводится к построению и
уничтожению связанных с ними объектов, но есть типы, для которых этого
мало. Иногда необходимо управлять всеми операциями копирования. Вернемся
к классу vector:

void f ()
{
vector v1 ( 100 );
vector v2 = v1; // построение нового вектора v2,
// инициализируемого v1
v1 = v2; // v2 присваивается v1
// ...
}

Должна быть возможность определить интерпретацию операций
инициализации v2 и присваивания v1. Например, в описании:

class vector
{
int v;
int sz;
public:
// ...
void operator = ( const vector & ); // присваивание
vector ( const vector & ); // инициализация
};

указывается, что присваивание и инициализация объектов типа vector
должны выполняться с помощью определенных пользователем операций.
Присваивание можно определить так:

void vector::operator = ( const vector & a )
// контроль размера и копирование элементов
{
if ( sz != a.sz )
error ( "недопустимый размер вектора для =" );
for ( int i = 0; i < sz; i++ ) v [ i ] = a.v [ i ];
}

Поскольку эта операция использует для присваивания "старое значение"
вектора, операция инициализации должна задаваться другой функцией,
например, такой:

vector::vector ( const vector & a )
// инициализация вектора значением другого вектора
{
sz = a.sz; // размер тот же
v = new int [ sz ]; // выделить память для массива
for ( int i = 0; i < sz; i++ ) //копирование элементов
v [ i ] = a.v [ i ];
}

В языке С++ конструктор вида T(const T&) называется конструктором
копирования для типа T. Любую инициализацию объектов типа T он выполняет с
помощью значения некоторого другого объекта типа T. Помимо явной
инициализации конструкторы вида T(const T&) используются для передачи
параметров по значению и получения возвращаемого функцией значения.


1.4.3 Шаблоны типа



Зачем программисту может понадобиться определить такой тип, как вектор
целых чисел? Как правило, ему нужен вектор из элементов, тип которых
неизвестен создателю класса Vector. Следовательно, надо суметь определить
тип вектора так, чтобы тип элементов в этом определении участвовал как
параметр, обозначающий "реальные" типы элементов:

template < class T > class Vector
{ // вектор элементов типа T
T
v;
int sz;
public:
Vector ( int s )
{
if ( s <= 0 )
error ( "недопустимый для Vector размер" );
v = new T [ sz = s ];
// выделить память для массива s типа T
}
T & operator [] ( int i );
int size () { return sz; }
// ...
};
sageАноним 10/05/15 Вск 03:34:38 #224 №92628214 DELETED
Таково определение шаблона типа. Он задает способ получения семейства
сходных классов. В нашем примере шаблон типа Vector показывает, как можно
получить класс вектор для заданного типа его элементов. Это описание
отличается от обычного описания класса наличием начальной конструкции
template<class T>, которая и показывает, что описывается не класс, а
шаблон типа с заданным параметром-типом (здесь он используется как тип
элементов). Теперь можно определять и использовать вектора разных типов:

void f ()
{
Vector < int > v1 ( 100 ); // вектор из 100 целых
Vector < complex > v2 ( 200 ); // вектор из 200
// комплексных чисел
v2 [ i ] = complex ( v1 [ x ], v1 [ y ] );
// ...
}

Возможности, которые реализует шаблон типа, иногда называются
параметрическими типами или генерическими объектами. Оно сходно с
возможностями, имеющимися в языках Clu и Ада. Использование шаблона типа
не влечет за собой каких-либо дополнительных расходов времени по сравнению
с использованием класса, в котором все типы указаны непосредственно.


1.4.4 Обработка особых ситуаций



По мере роста программ, а особенно при активном использовании
библиотек появляется необходимость стандартной обработки ошибок (или, в
более широком смысле, "особых ситуаций"). Языки Ада, Алгол-68 и Clu
поддерживают стандартный способ обработки особых ситуаций.

Снова вернемся к классу vector. Что нужно делать, когда операции
индексации передано значение индекса, выходящее за границы массива?
Создатель класса vector не знает, на что рассчитывает пользователь в таком
случае, а пользователь не может обнаружить подобную ошибку (если бы мог,
то эта ошибка вообще не возникла бы). Выход такой: создатель класса
обнаруживает ошибку выхода за границу массива, но только сообщает о ней
неизвестному пользователю. Пользователь сам принимает необходимые меры.
Например:

class vector {
// определение типа возможных особых ситуаций
class range { };
// ...
};

Вместо вызова функции ошибки в функции vector::operator[]() можно
перейти на ту часть программы, в которой обрабатываются особые ситуации.
Это называется "запустить особую ситуацию" ("throw the exception"):

int & vector::operator [] ( int i )
{
if ( i < 0 || sz <= i ) throw range ();
return v [ i ];
}

В результате из стека будет выбираться информация, помещаемая туда при
вызовах функций, до тех пор, пока не будет обнаружен обработчик особой
ситуации с типом range для класса вектор (vector::range); он и будет
выполняться.
sageАноним 10/05/15 Вск 03:34:54 #225 №92628227 DELETED
Обработчик особых ситуаций можно определить только для специального
блока:

void f ( int i )
{
try
{
// в этом блоке обрабатываются особые ситуации
// с помощью определенного ниже обработчика
vector v ( i );
// ...
v [ i + 1 ] = 7; // приводит к особой ситуации range
// ...
g (); // может привести к особой ситуации range
// на некоторых векторах
}
catch ( vector::range )
{
error ( "f (): vector range error" );
return;
}
}

Использование особых ситуаций делает обработку ошибок более
упорядоченной и понятной. Обсуждение и подробности отложим до главы 9.


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 ();
// ...
}
sageАноним 10/05/15 Вск 03:35:22 #226 №92628245 DELETED
Поскольку интерфейс стека ничего не сообщает о его представлении, от
пользователей стека полностью скрыты детали его реализации.

Можно предложить несколько различных реализаций стека. Например, стек
может быть массивом:

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()) полностью огражден от деталей их реализации.
Платой за подобную гибкость является то, что все операции над стеками
должны быть виртуальными функциями.
sageАноним 10/05/15 Вск 03:35:46 #227 №92628261 DELETED
Поддержку объектно-ориентированного программирования обеспечивают
классы вместе с механизмом наследования, а также механизм вызова
функций-членов в зависимости от истинного типа объекта (дело в том, что
возможны случаи, когда этот тип неизвестен на стадии трансляции). Особенно
важную роль играет механизм вызова функций-членов. Не менее важны
средства, поддерживающие абстракцию данных (о них мы говорили ранее). Все
доводы в пользу абстракции данных и базирующихся на ней методов, которые
позволяют естественно и красиво работать с типами, действуют и для языка,
поддерживающего объектно-ориентированное программирование. Успех обоих
методов зависит от способа построения типов, от того, насколько они
просты, гибки и эффективны. Метод объектно-ориентированного
программирования позволяет определять более общие и гибкие
пользовательские типы по сравнению с теми, которые получаются, если
использовать только абстракцию данных.


1.5.1 Механизм вызова



Основное средство поддержки объектно-ориентированного программирования
- это механизм вызова функции-члена для данного объекта, когда истинный
тип его на стадии трансляции неизвестен. Пусть, например, есть указатель
p. Как происходит вызов p->rotate(45)? Поскольку С++ базируется на
статическом контроле типов, задающее вызов выражение имеет смысл только
при условии, что функция rotate() уже была описана. Далее, из обозначения
p->rotate() мы видим, что p является указателем на объект некоторого
класса, а rotate должна быть членом этого класса. Как и при всяком
статическом контроле типов проверка корректности вызова нужна для того,
чтобы убедиться (насколько это возможно на стадии трансляции), что типы в
программе используются непротиворечивым образом. Тем самым гарантируется,
что программа свободна от многих видов ошибок.

Итак, транслятору должно быть известно описание класса, аналогичное
тем, что приводились в $$1.2.5:

class shape
{
// ...
public:
// ...
virtual void rotate ( int );
// ...
};

а указатель p должен быть описан, например, так:

T * p;

где T - класс shape или производный от него класс. Тогда транслятор
видит, что класс объекта, на который настроен указатель p, действительно
имеет функцию rotate(), а функция имеет параметр типа int. Значит,
p->rotate(45) корректное выражение.

Поскольку shape::rotate() была описана как виртуальная функция, нужно
использовать механизм вызова виртуальной функции. Чтобы узнать, какую
именно из функций rotate следует вызвать, нужно до вызова получить из
объекта некоторую служебную информацию, которая была помещена туда при его
создании. Как только установлено, какую функцию надо вызвать, допустим
circle::rotate, происходит ее вызов с уже упоминавшимся контролем типа.
Обычно в качестве служебной информации используется таблица адресов
функций, а транслятор преобразует имя rotate в индекс этой таблицы. С
учетом этой таблицы объект типа shape можно представить так:


center
vtbl:
color &X::draw
&Y::rotate
sageАноним 10/05/15 Вск 03:36:02 #228 №92628277 DELETED
Функции из таблицы виртуальных функций vtbl позволяют правильно
работать с объектом даже в тех случаях, когда в вызывающей функции
неизвестны ни таблица vtbl, ни расположение данных в части объекта,
обозначенной ... . Здесь как X и Y обозначены имена классов, в которые
входят вызываемые функции. Для объекта circle оба имени X и Y есть circle.
Вызов виртуальной функции может быть по сути столь же эффективен, как
вызов обычной функции.


1.5.2 Проверка типа



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

template < class T > class stack
{
T p;
int sz;
public:
stack ( int );
~stack ();

void push ( T );
T & pop ();
};

Не ослабляя статического контроля типов, можно использовать такой стек
для хранения указателей на объекты типа plane (самолет):

stack < plane
> cs ( 200 );

void f ()
{
cs.push ( new Saab900 ); // Ошибка при трансляции :
// требуется plane, а передан car
cs.push ( new Saab37B );
// прекрасно: Saab 37B - на самом
// деле самолет, т.е. типа plane
cs.pop () -> takeoff ();
cs.pop () -> takeoff ();
}

Если статического контроля типов нет, приведенная выше ошибка
обнаружится только при выполнении программы:

// пример динамическое контроля типа
// вместо статического; это не С++
Stack s; // стек может хранить указатели на объекты
// произвольного типа
void f ()
{
s.push ( new Saab900 );
s.push ( new Saab37B );
s.pop () -> takeoff (); // прекрасно: Saab 37B - самолет
cs.pop () -> takeoff (); // динамическая ошибка:
// машина не может взлететь
}

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

Рассчитывая на статический контроль типов и вызов виртуальных функций,
мы приходим к иному стилю программирования, чем надеясь только на
динамический контроль типов. Класс в С++ задает строго определенный
интерфейс для множества объектов этого и любого производного класса, тогда
как в Smalltalk класс задает только минимально необходимое число операций,
и пользователь вправе применять незаданные в классе операции. Иными
словами, класс в С++ содержит точное описание операций, и пользователю
гарантируется, что только эти операции транслятор сочтет допустимыми.
sageАноним 10/05/15 Вск 03:36:22 #229 №92628286 DELETED
Если класс A является базовым классом для B, то B наследует атрибуты
A. т.е. B содержит A плюс еще что-то. С учетом этого становится очевидно,
что хорошо, когда класс B может наследовать из двух базовых классов A1 и
A2. Это называется множественным наследованием.

Приведем некий типичный пример множественного наследования. Пусть есть
два библиотечных класса displayed и task. Первый представляет задачи,
информация о которых может выдаваться на экран с помощью некоторого
монитора, а второй - задачи, выполняемые под управлением некоторого
диспетчера. Программист может создавать собственные классы, например,
такие:

class my_displayed_task: public displayed, public task
{
// текст пользователя
};

class my_task: public task {
// эта задача не изображается
// на экране, т.к. не содержит класс displayed
// текст пользователя
};

class my_displayed: public displayed
{
// а это не задача
// т.к. не содержит класс task
// текст пользователя
};

Если наследоваться может только один класс, то пользователю доступны
только два из трех приведенных классов. В результате либо получается
дублирование частей программы, либо теряется гибкость, а, как правило,
происходит и то, и другое. Приведенный пример проходит в С++ безо всяких
дополнительных расходов времени и памяти по сравнению с программами, в
которых наследуется не более одного класса. Статический контроль типов от
этого тоже не страдает.

Все неоднозначности выявляются на стадии трансляции:

class task
{
public:
void trace ();
// ...
};

class displayed
{
public:
void trace ();
// ...
};

class my_displayed_task:public displayed, public task
{
// в этом классе trace () не определяется
};

void g ( my_displayed_task p )
{
p -> trace (); // ошибка: неоднозначность
}

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

class my_displayed_task:public displayed, public task
{
// ...
public:
void trace ()
{
// текст пользователя
displayed::trace (); // вызов trace () из displayed
task::trace (); // вызов trace () из task
}
// ...
};

void g ( my_displayed_task
p )
{
p -> trace (); // теперь нормально
}


1.5.4 Инкапсуляция



Пусть члену класса (неважно функции-члену или члену, представляющему
данные) требуется защита от "несанкционированного доступа". Как разумно
ограничить множество функций, которым такой член будет доступен? Очевидный
ответ для языков, поддерживающих объектно-ориентированное
программирование, таков: доступ имеют все операции, которые определены для
этого объекта, иными словами, все функции-члены. Например:

class window
{
// ...
protected:
Rectangle inside;
// ...
};

class dumb_terminal : public window
{
// ...
public:
void prompt ();
// ...
};
sageАноним 10/05/15 Вск 03:36:41 #230 №92628299 DELETED
Здесь в базовом классе window член inside типа Rectangle описывается
как защищенный (protected), но функции-члены производных классов,
например, dumb_terminal::prompt(), могут обратиться к нему и выяснить, с
какого вида окном они работают. Для всех других функций член
window::inside недоступен.

В таком подходе сочетается высокая степень защищенности
(действительно, вряд ли вы "случайно" определите производный класс) с
гибкостью, необходимой для программ, которые создают классы и используют
их иерархию (действительно, "для себя" всегда можно в производных классах
предусмотреть доступ к защищенным членам).

Неочевидное следствие из этого: нельзя составить полный и
окончательный список всех функций, которым будет доступен защищенный член,
поскольку всегда можно добавить еще одну, определив ее как функцию-член в
новом производном классе. Для метода абстракции данных такой подход часто
бывает мало приемлемым. Если язык ориентируется на метод абстракции
данных, то очевидное для него решение - это требование указывать в
описании класса список всех функций, которым нужен доступ к члену. В С++
для этой цели используется описание частных (private) членов. Оно
использовалось и в приводившихся описаниях классов complex и shape.

Важность инкапсуляции, т.е. заключения членов в защитную оболочку,
резко возрастает с ростом размеров программы и увеличивающимся разбросом
областей приложения. В $$6.6 более подробно обсуждаются возможности языка
по инкапсуляции.


1.6 Пределы совершенства



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

Основная трудность для языка, который создавался в расчете на методы
упрятывания данных, абстракции данных и объектно-ориентированного
программирования, в том, что для того, чтобы быть языком общего
назначения, он должен:

- идти на традиционных машинах;

- сосуществовать с традиционными операционными системами и языками;

- соперничать с традиционными языками программирования в эффективности
выполнения программы;

- быть пригодным во всех основных областях приложения.

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

Далее, нельзя рассчитывать на широкое использование искомого языка
программирования как языка общего назначения, если реализация его целиком
полагается на возможности, которые отсутствуют в машинах с традиционной
архитектурой.
sageАноним 10/05/15 Вск 03:36:58 #231 №92628314 DELETED
Если не вводить в язык возможности низкого уровня, то придется для
основных задач большинства областей приложения использовать некоторые
языки низкого уровня, например С или ассемблер. Но С++ проектировался с
расчетом, что в нем можно сделать все, что допустимо на С, причем без
увеличения времени выполнения. Вообще, С++ проектировался, исходя из
принципа, что не должно возникать никаких дополнительных затрат времени и
памяти, если только этого явно не пожелает сам программист.

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

С++ проектировался для поддержки того принципа, что всякая программа
есть модель некоторых существующих в реальности понятий, а класс является
конкретным представлением понятия, взятого из области приложения ($$12.2).
Поэтому классы пронизывают всю программу на С++, и налагаются жесткие
требования на гибкость понятия класса, компактность объектов класса и
эффективность их использования. Если работать с классами будет неудобно
или слишком накладно, то они просто не будут использоваться, и программы
выродятся в программы на "лучшем С". Значит пользователь не сумеет
насладиться теми возможностями, ради которых, собственно, и создавался
язык.


* ГЛАВА 2. ОПИСАНИЯ И КОНСТАНТЫ




"Совершенство достижимо только в момент
краха".

(С.Н. Паркинсон)


В данной главе описаны основные типы (char, int, float и т.д.) и
способы построения на их основе новых типов (функций, векторов, указателей
и т.д.). Описание вводит в программу имя, указав его тип и, возможно,
начальное значение. В этой главе вводятся такие понятия, как описание и
определение, типы, область видимости имен, время жизни объектов.
Даются обозначения литеральных констант С++ и способы задания
символических констант. Приводятся примеры, которые просто
демонстрируют возможности языка. Более осмысленные примеры, иллюстрирующие
возможности выражений и операторов языка С++, будут приведены в следующей
главе. В этой главе лишь упоминаются средства для определения
пользовательских типов и операций над ними. Они обсуждаются в главах 5 и 7.
sageАноним 10/05/15 Вск 03:37:14 #232 №92628327 DELETED
Имя (идентификатор) следует описать прежде, чем оно будет использоваться
в программе на С++. Это означает, что нужно указать его тип, чтобы
транслятор знал, к какого вида объектам относится имя. Ниже приведены
несколько примеров, иллюстрирующих все разнообразие описаний:

char ch;
int count = 1;
char name = "Njal";
struct complex { float re, im; };
complex cvar;
extern complex sqrt(complex);
extern int error_number;
typedef complex point;
float real(complex
p) { return p->re; };
const double pi = 3.1415926535897932385;
struct user;
template<class T> abs(T a) { return a<0 ? -a : a; }
enum beer { Carlsberg, Tuborg, Thor };

Из этих примеров видно, что роль описаний не сводится лишь к привязке
типа к имени. Большинство указанных описаний одновременно являются
определениями, т.е. они создают объект, на который ссылается имя.
Для ch, count, name и cvar таким объектом является элемент памяти
соответствующего размера. Этот элемент будет использоваться как
переменная, и говорят, что для него отведена память. Для real подобным
объектом будет заданная функция.
Для константы pi объектом будет число 3.1415926535897932385.
Для complex объектом будет новый тип. Для point объектом является
тип complex, поэтому point становится синонимом complex. Следующие
описания уже не являются определениями:

extern complex sqrt(complex);
extern int error_number;
struct user;

Это означает, что объекты, введенные ими, должны быть определены
где-то в другом месте программы. Тело функции sqrt должно быть указано
в каком-то другом описании. Память для переменной error_number типа
int должна выделяться в результате другого описания error_number.
Должно быть и какое-то другое описание типа user, из которого можно
понять, что это за тип. В программе на языке С++ должно быть только
одно определение каждого имени, но описаний может быть много. Однако все
описания должны быть согласованы по типу вводимого в них объекта.
Поэтому в приведенном ниже фрагменте содержатся две ошибки:

int count;
int count; // ошибка: переопределение

extern int error_number;
extern short error_number; // ошибка: несоответствие типов

Зато в следующем фрагменте нет ни одной ошибки (об использовании
extern см. #4.2):

extern int error_number;
extern int error_number;


В некоторых описаниях указываются "значения" объектов, которые они
определяют:

struct complex { float re, im; };
typedef complex point;
float real(complex p) { return p->re };
const double pi = 3.1415926535897932385;

Для типов, функций и констант "значение" остается неизменным;
для данных, не являющихся константами, начальное значение может
впоследствии изменяться:

int count = 1;
char
name = "Bjarne";
//...
count = 2;
name = "Marian";

Из всех определений только следующее не задает значения:

char ch;

Всякое описание, которое задает значение, является определением.
sageАноним 10/05/15 Вск 03:37:30 #233 №92628340 DELETED
Описанием определяется область видимости имени. Это значит, что
имя может использоваться только в определенной части текста программы.
Если имя описано в функции (обычно его называют "локальным именем"), то
область видимости имени простирается от точки описания
до конца блока, в котором появилось это описание. Если имя не находится
в описании функции или класса (его обычно называют "глобальным именем"),
то область видимости простирается от точки описания до конца файла,
в котором появилось это описание.
Описание имени в блоке может скрывать описание в объемлющем блоке или
глобальное имя; т.е. имя может быть переопределено так, что оно будет
обозначать другой объект внутри блока. После выхода из блока прежнее
значение имени (если оно было) восстанавливается. Приведем пример:

int x; // глобальное x

void f()
{
int x; // локальное x скрывает глобальное x
x = 1; // присвоить локальному x
{
int x; // скрывает первое локальное x
x = 2; // присвоить второму локальному x
}
x = 3; // присвоить первому локальному x
}

int* p = &x; // взять адрес глобального x


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

int x;

void f2()
{
int x = 1; // скрывает глобальное x
::x = 2; // присваивание глобальному x
}

Возможность использовать скрытое локальное имя отсутствует.
Область видимости имени начинается в точке его описания (по
окончании описателя, но еще до начала инициализатора - см. $$R.3.2). Это
означает, что имя можно использовать даже до того, как задано его
начальное значение. Например:

int x;

void f3()
{
int x = x; // ошибочное присваивание
}

Такое присваивание недопустимо и лишено смысла. Если вы попытаетесь
транслировать эту программу, то получите предупреждение: "использование
до задания значения". Вместе с тем, не применяя оператора ::, можно
использовать одно и то же имя для обозначения двух различных объектов
блока. Например:

int x = 11;

void f4() // извращенный пример
{
int y = x; // глобальное x
int x = 22;
y = x; // локальное x
}

Переменная y инициализируется значением глобального x, т.е. 11,
а затем ей присваивается значение локальной переменной x, т.е. 22.
Имена формальных параметров функции считаются описанными в самом
большом блоке функции, поэтому в описании ниже есть ошибка:

void f5(int x)
{
int x; // ошибка
}

Здесь x определено дважды в одной и той же области видимости.
Это хотя и не слишком редкая, но довольно тонкая ошибка.
sageАноним 10/05/15 Вск 03:37:47 #234 №92628351 DELETED
Можно выделять память для "переменных", не имеющих имен, и
использовать эти переменные.
Возможно даже присваивание таким странно выглядящим "переменным",
например, *p[a+10]=7. Следовательно, есть потребность именовать
"нечто хранящееся в памяти". Можно привести подходящую цитату из
справочного руководства: "Любой объект - это некоторая область
памяти, а адресом называется выражение, ссылающееся на объект или
функцию" ($$R.3.7). Слову адрес (lvalue - left value, т.е. величина
слева) первоначально приписывался смысл "нечто, что может в
присваивании стоять слева". Адрес может ссылаться и на константу
(см. $$2.5). Адрес, который не был описан со спецификацией const,
называется изменяемым адресом.

2.1.3 Время жизни объектов



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

int a = 1;

void f()
{
int b = 1; // инициализируется при каждом вызове f()
static int c = a; // инициализируется только один раз
cout << " a = " << a++
<< " b = " << b++
<< " c = " << c++ << '\n';
}

int main()
{
while (a < 4) f();
}

Здесь программа выдаст такой результат:

a = 1 b = 1 c = 1
a = 2 b = 1 c = 2
a = 3 b = 1 c = 3

''Из примеров этой главы для краткости изложения исключена
макрокоманда #include <iostream>. Она нужна лишь в тех из них, которые
выдают результат.

Операция "++" является инкрементом, т. е. a++ означает: добавить 1
к переменной a.

Глобальная переменная или локальная переменная static, которая не была
явно инициализирована, инициализируется неявно нулевым значением (#2.4.5).
Используя операции new и delete, программист может создавать
объекты, временем жизни которых он управляет сам (см. $$3.2.6).
sageАноним 10/05/15 Вск 03:38:03 #235 №92628363 DELETED
Имя (идентификатор) является последовательностью букв или цифр.
Первый символ должен быть буквой. Буквой считается и символ
подчеркивания _. Язык С++ не ограничивает число символов в имени.
Но в реализацию входят программные компоненты, которыми создатель
транслятора управлять не может (например, загрузчик), а они,
к сожалению, могут устанавливать ограничения. Кроме того, некоторые
системные программы, необходимые для выполнения программы на С++, могут
расширять или сужать множество символов, допустимых в идентификаторе.
Расширения (например, использование $ в имени) могут нарушить
переносимость программы. Нельзя использовать в качестве имен
служебные слова С++ (см. $$R.2.4), например:


hello this_is_a_most_unusially_long_name
DEFINED foO bAr u_name HorseSense
var0 var1 CLASS _class ___

Теперь приведем примеры последовательностей символов, которые не могут
использоваться как идентификаторы:

012 a fool $sys class 3var
pay.due foo~bar .name if

Заглавные и строчные буквы считаются различными, поэтому Count и
count - разные имена. Но выбирать имена, почти не отличающиеся
друг от друга, неразумно. Все имена, начинающиеся с символа
подчеркивания, резервируются для использования в самой реализации
или в тех программах, которые выполняются совместно с рабочей,
поэтому крайне легкомысленно вставлять такие имена в
свою программу.
При разборе программы транслятор всегда стремится выбрать самую
длинную последовательность символов, образующих имя, поэтому var10
- это имя, а не идущие подряд имя var и число 10. По той же причине
elseif - одно имя (служебное), а не два служебных имени else и if.

2.3 ТИПЫ



С каждым именем (идентификатором) в программе связан тип. Он
задает те операции, которые могут применяться к имени (т.е. к объекту,
который обозначает имя), а также интерпретацию этих операций.
Приведем примеры:

int error_number;
float real(complex p);

Поскольку переменная error_number описана как int (целое), ей можно
присваивать, а также можно использовать ее значения в арифметических
выражениях. Функцию real можно вызывать с параметром, содержащим
адрес complex. Можно получать адреса и переменной, и функции.
Некоторые имена, как в нашем примере int и complex, являются именами
типов. Обычно имя типа нужно, чтобы задать в описании типа некоторое
другое имя. Кроме того, имя типа может использоваться
в качестве операнда в операциях sizeof (с ее помощью определяют
размер памяти, необходимый для объектов этого типа) и new (с ее
помощью можно разместить в свободной памяти объект этого типа).
Например:

int main()
{
int
p = new int;
cout << "sizeof(int) = " << sizeof(int) '\n';
}

Еще имя типа может использоваться в операции явного преобразования
одного типа к другому ($$3.2.5), например:

float f;
char* p;
//...
long ll = long(p); // преобразует p в long
int i = int(f); // преобразует f в int
Аноним 10/05/15 Вск 03:38:13 #236 №92628373 
опаньки, бамп
sageАноним 10/05/15 Вск 03:38:21 #237 №92628380 DELETED
Основные типы С++ представляют самые распространенные единицы памяти
машин и все основные способы работы с ними. Это:

char
short int
int
long int

Перечисленные типы используются для представления различного
размера целых. Числа с плавающей точкой представлены типами:

float
double
long double

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

unsigned char
unsigned short int
unsigned int
unsigned long int

Ниже приведены типы, которые используются для явного задания знаковых
типов:

signed char
signed short int
signed int
signed long int

Поскольку по умолчанию значения типа int считаются знаковыми, то
соответствующие типы с signed являются синонимами типов без
этого служебного слова.
Но тип signed char представляет особый интерес: все 3 типа - unsigned char,
signed char и просто char считаются различными (см. также $$R.3.6.1).
Для краткости (и это не влечет никаких последствий) слово int можно
не указывать в многословных типах, т.е. long означает long int, unsigned -
unsigned int. Вообще, если в описании не указан тип, то предполагается,
что это int. Например, ниже даны два определения объекта типа int:

const a = 1; // небрежно, тип не указан
static x; // тот же случай

Все же обычно пропуск типа в описании в надежде, что по умолчанию
это будет тип int, считается дурным стилем. Он может вызвать тонкий и
нежелательный эффект (см. $$R.7.1).
Для хранения символов и работы с ними наиболее подходит тип char.
Обычно он представляет байт из 8 разрядов. Размеры всех объектов в С++
кратны размеру char, и по определению значение sizeof(char) тождественно 1.
В зависимости от машины значение типа char может быть знаковым
или беззнаковым целым. Конечно, значение типа unsigned char всегда
беззнаковое, и, задавая явно этот тип, мы улучшаем переносимость
программы. Однако, использование unsigned char вместо char может
снизить скорость выполнения программы. Естественно, значение
типа signed char всегда знаковое.
В язык введено несколько целых, несколько беззнаковых типов
и несколько типов с плавающей точкой, чтобы программист мог полнее
использовать возможности системы команд. У многих машин
значительно различаются размеры выделяемой памяти, время доступа
и скорость вычислений для значений различных основных типов.
Как правило, зная особенности конкретной машины, легко выбрать
оптимальный основной тип (например, один из типов int) для данной
переменной. Однако, написать действительно переносимую программу,
использующую такие возможности низкого уровня, непросто. Для размеров
основных типов выполняются следующие соотношения:


1==sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long)

sizeof(float)<=sizeof(double)<=sizeof(long double)

sizeof(I)==sizeof(signed I)==sizeof(unsigned I)

Здесь I может быть типа char, short, int или long. Помимо этого
гарантируется, что char представлен не менее, чем 8 разрядами, short
- не менее, чем 16 разрядами и long - не менее, чем 32 разрядами. Тип char
достаточен для представления любого символа из набора символов
данной машины. Но это означает только то, что тип char может
представлять целые в диапазоне 0..127. Предположить большее -
рискованно.
Типы беззнаковых целых больше всего подходят для таких программ, в
которых память рассматривается как массив разрядов. Но, как
правило, использование unsigned вместо int, не дает ничего хорошего,
хотя таким образом рассчитывали выиграть еще один разряд для
представления положительных целых. Описывая переменную как unsigned,
нельзя гарантировать, что она будет только положительной, поскольку
допустимы неявные преобразования типа, например:

unsigned surprise = -1;

Это определение допустимо (хотя компилятор может выдать предупреждение
о нем).
sageАноним 10/05/15 Вск 03:38:38 #238 №92628397 DELETED
В присваивании и выражении основные типы могут совершенно свободно
использоваться совместно. Значения преобразовываются всюду, где
это возможно, таким образом, чтобы информация не терялась. Точные
правила преобразований даны в $$R.4 и $$R.5.4.
Все-таки есть ситуации, когда информация может быть потеряна или
даже искажена. Потенциальным источником таких ситуаций становятся
присваивания, в которых значение одного типа присваивается значению
другого типа, причем в представлении последнего используется
меньше разрядов. Допустим, что следующие присваивания выполняются
на машине, в которой целые представляются в дополнительном коде, и символ
занимает 8 разрядов:

int i1 = 256+255;
char ch = i1 // ch == 255
int i2 = ch; // i2 == ?

В присваивании ch=i1 теряется один разряд (и самый важный!), а когда
мы присваиваем значение переменной i2, у переменной ch значение "все
единицы", т.е. 8 единичных разрядов. Но какое значение примет i2? На
машине DEC VAX, в которой char представляет знаковые значения, это будет
-1, а на машине Motorola 68K, в которой char - беззнаковый,
это будет 255. В С++ нет динамических средств контроля
подобных ситуаций, а контроль на этапе трансляции вообще слишком
сложен, поэтому надо быть осторожными.

2.3.3 Производные типы



Исходя из основных (и определенных пользователем) типов, можно с
помощью следующих операций описания:

указатель
& ссылка
[] массив
() функция

а также с помощью определения структур, задать другие, производные типы.
Например:

int
a;
float v[10];
char p[20]; // массив из 20 символьных указателей
void f(int);
struct str { short length; char
p; };

Правила построения типов с помощью этих операций подробно объяснены
в $$R.8. Ключевая идея состоит в том, что описание объекта производного
типа должно отражать его использование, например:

int v[10]; // описание вектора
i = v[3]; // использование элемента вектора

int p; // описание указателя
i =
p; // использование указуемого объекта

Обозначения, используемые для производных типов, достаточно трудны
для понимания лишь потому, что операции и & являются префиксными, а
[] и () - постфиксными. Поэтому в задании типов, если приоритеты
операций не отвечают цели, надо ставить скобки. Например, приоритет
операции [] выше, чем у
, и мы имеем:

int v[10]; // массив указателей
int (
p)[10]; // указатель массива

Большинство людей просто запоминает, как выглядят наиболее часто
употребляемые типы.
Можно описать сразу несколько имен в одном описании. Тогда оно содержит
вместо одного имени список отделяемых друг от друга запятыми
имен. Например, можно так описать две переменные целого типа:

int x, y; // int x; int y;

Когда мы описываем производные типы, не надо забывать, что операции
описаний применяются только к данному имени (а вовсе не ко всем
остальным именам того же описания). Например:

int p, y; // int p; int y; НО НЕ int y;
int x,
p; // int x; int p;
int v[10],
p; // int v[10]; int* p;

Но такие описания запутывают программу, и, возможно, их следует
избегать.
sageАноним 10/05/15 Вск 03:38:56 #239 №92628409 DELETED
Тип void синтаксически эквивалентен основным типам, но использовать
его можно только в производном типе. Объектов типа void не существует.
С его помощью задаются указатели на объекты неизвестного типа или
функции, невозвращающие значение.

void f(); // f не возвращает значения
void pv; // указатель на объект неизвестного типа

Указатель произвольного типа можно присваивать переменной типа void
.
На первый взгляд этому трудно найти применение, поскольку для void
недопустимо косвенное обращение (разыменование). Однако, именно
на этом ограничении основывается использование типа void
. Он
приписывается параметрам функций, которые не должны знать истинного
типа этих параметров. Тип void имеют также бестиповые объекты,
возвращаемые функциями.
Для использования таких объектов нужно выполнить явную операцию
преобразования типа. Такие функции обычно находятся на самых нижних
уровнях системы, которые управляют аппаратными
ресурсами. Приведем пример:

void
malloc(unsigned size);
void free(void);

void f() // распределение памяти в стиле Си
{
int
pi = (int)malloc(10sizeof(int));
char pc = (char)malloc(10);
//...
free(pi);
free(pc);
}

Обозначение: (тип) выражение - используется для задания операции
преобразования выражения к типу, поэтому перед присваиванием
pi тип void, возвращаемый в первом вызове malloc(), преобразуется
в тип int. Пример записан в архаичном стиле; лучший стиль
управления размещением в свободной памяти показан в $$3.2.6.

2.3.5 Указатели



Для большинства типов T указатель на T имеет тип T
. Это значит, что
переменная типа T может хранить адрес объекта типа T. Указатели на
массивы и функции, к сожалению, требуют более сложной записи:

int
pi;
char cpp; // указатель на указатель на char
int (vp)[10]; // указатель на массив из 10 целых
int (
fp)(char, char); // указатель на функцию с параметрами
// char и char
, возвращающую int

Главная операция над указателями - это косвенное обращение
(разыменование), т.е. обращение к объекту, на который настроен
указатель. Эту операцию обычно называют просто косвенностью.
Операция косвенности является префиксной унарной операцией.
Например:

char c1 = 'a';
char
p = &c1; // p содержит адрес c1
char c2 = p; // c2 = 'a'

Переменная, на которую указывает p,- это c1, а значение, которое
хранится в c1, равно 'a'. Поэтому присваиваемое c2 значение
p
есть 'a'.
Над указателями можно выполнять и некоторые арифметические операции.
Ниже в качестве примера представлена функция, подсчитывающая число
символов в строке, заканчивающейся нулевым символом (который
не учитывается):

int strlen(char p)
{
int i = 0;
while (
p++) i++;
return i;
}

Можно определить длину строки по-другому: сначала найти ее конец, а затем
вычесть адрес начала строки из адреса ее конца.

int strlen(char p)
{
char
q = p;
while (*q++) ;
return q-p-1;
}

Широко используются указатели на функции; они особо обсуждаются
в $$4.6.9
sageАноним 10/05/15 Вск 03:39:20 #240 №92628431 DELETED
Для типа T T[size] является типом "массива из size элементов типа T".
Элементы индексируются от 0 до size-1. Например:

float v[3]; // массив из трех чисел с плавающей точкой:
// v[0], v[1], v[2]
int a[2][5]; // два массива, из пяти целых каждый
char vpc; // массив из 32 символьных указателей

Можно следующим образом записать цикл, в котором печатаются целые
значения прописных букв:

extern "C" int strlen(const char
); // из <string.h>

char alpha[] = "abcdefghijklmnopqrstuvwxyz";

main()
{
int sz = strlen(alpha);

for (int i=0; i<sz; i++) {
char ch = alpha;
cout << '\''<< ch << '\''
<< " = " <<int(ch)
<< " = 0" << oct(ch)
<< " = 0x" << hex(ch) << '\n';
}
}

Здесь функции oct() и hex() выдают свой параметр целого типа
в восьмеричном и шестнадцатеричном виде соответственно. Обе функции
описаны в <iostream.h>. Для подсчета числа символов в alpha
используется функция strlen() из <string.h>, но вместо нее можно
было использовать размер массива alpha ($$2.4.4). Для множества
символов ASCII результат будет таким:

'a' = 97 = 0141 = 0x61
'b' = 98 = 0142 = 0x62
'c' = 99 = 0143 = 0x63
...

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

char v[9];
v = "a string"; // ошибка

Классы позволяют реализовать представление строк с большим набором
операций (см. $$7.10).
Очевидно, что строки пригодны только для инициализации символьных
массивов; для других типов приходится использовать более сложную
запись. Впрочем, она может использоваться и для символьных массивов.
Например:

int v1[] = { 1, 2, 3, 4 };
int v2[] = { 'a', 'b', 'c', 'd' };

char v3[] = { 1, 2, 3, 4 };
char v4[] = { 'a', 'b', 'c', 'd' };

Здесь v3 и v4 - массивы из четырех (а не пяти) символов; v4 не оканчивается
нулевым символом, как того требуют соглашение о строках и большинство
библиотечных функций. Используя такой массив char мы сами
готовим почву для будущих ошибок.
Многомерные массивы представлены как массивы массивов. Однако нельзя
при задании граничных значений индексов использовать, как это делается
в некоторых языках, запятую. Запятая - это особая операция для
перечисления выражений (см. $$3.2.2). Можно попробовать задать такое
описание:

int bad[5,2]; // ошибка

или такое

int v[5][2];
int bad = v[4,1]; // ошибка
int good = v[4][1]; // правильно

Ниже описывается
массив из двух элементов, каждый из которых является, в свою очередь,
массивом из 5 элементов типа char:

char v[2][5];

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

char v[2][5] = {
{ 'a', 'b', 'c', 'd', 'e' },
{ '0', '1', '2', '3', '4' }
};

main() {
for (int i = 0; i<2; i++) {
for (int j = 0; j<5; j++)
cout << "v[" << i << "][" << j
<< "]=" << v[j] << " ";
cout << '\n';

}
}

В результате получим:

v[0][0]=a v[0][1]=b v[0][2]=c v[0][3]=d v[0][4]=e
v[1][0]=0 v[1][1]=1 v[1][2]=2 v[1][3]=3 v[1][4]=4
sageАноним 10/05/15 Вск 03:39:40 #241 №92628446 DELETED
Указатели и массивы в языке Си++ тесно связаны. Имя массива можно
использовать как указатель на его первый элемент, поэтому пример с
массивом alpha можно записать так:

int main()
{
char alpha[] = "abcdefghijklmnopqrstuvwxyz";
char p = alpha;
char ch;

while (ch =
p++)
cout << ch << " = " << int (ch)
<< " = 0" << oct(ch) << '\n';
}

Можно также задать описание p следующим образом:

char p = &alpha[0];

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

void f()
{
extern "C" int strlen(const char
); // из <string.h>
char v[] = "Annemarie";
char p = v;
strlen(p);
strlen(v);
}

Но в том и загвоэдка, что обойти это нельзя: не существует способа так
описать функцию, чтобы при ее вызове массив v копировался ($$4.6.3).
Результат применения к указателям арифметических операций +,
-, ++ или -- зависит от типа указуемых объектов. Если такая операция
применяется к указателю p типа T
, то считается, что p указывает на
массив объектов типа T. Тогда p+1 обозначает следующий элемент
этого массива, а p-1 - предыдущий элемент. Отсюда следует, что
значение (адрес) p+1 будет на sizeof(T) байтов больше, чем значение
p. Поэтому в следующей программе

main()
{
char cv[10];
int iv[10];

char pc = cv;
int
pi = iv;

cout << "char " << long(pc+1)-long(pc) << '\n';
cout << "int
" << long(pi+1)-long(pi) << '\n';
}

с учетом того, что на машине автора (Maccintosh) символ занимает один байт,
а целое - четыре байта, получим:

char 1
int
4

Перед вычитанием указатели были явной операцией преобразованы
к типу long ($$3.2.5). Он использовался для преобразования вместо
"очевидного" типа int, поскольку в некоторых реализациях языка С++
указатель может не поместиться в тип int (т.е. sizeof(int)<sizeof(char)).
Вычитание указателей определено только в том случае, когда
они оба указывают на один и тот же массив (хотя в языке нет
возможностей гарантировать этот факт). Результат вычитания одного
указателя из другого равен числу (целое) элементов массива, находящихся
между этими указателями. Можно складывать с указателем или вычитать из него
значение целого типа; в обоих случаях результатом будет указатель.
Если получится значение, не являющееся указателем на элемент того же
массива, на который был настроен исходный указатель (или указателем на
следующий за массивом элемент), то результат использования такого
значения неопределен. Приведем пример:

void f()
{
int v1[10];
int v2[10];

int i = &v1[5]-&v1[3]; // 2
i = &v1[5]-&v2[3]; // неопределенный результат

int
p = v2+2; // p == &v2[2]
p = v2-2; // *p неопределено
}

Как правило, сложных арифметических операций с указателями не требуется
и лучше всего их избегать.
Следует сказать, что в
большинстве реализаций языка С++ нет контроля над границами массивов.
Описание массива не является самодостаточным, поскольку необязательно
в нем будет храниться число элементов массива.
Понятие массива в С является, по сути, понятием языка низкого
уровня. Классы помогают развить его (см. $$1.4.3).
sageАноним 10/05/15 Вск 03:39:58 #242 №92628464 DELETED
Массив представляет собой совокупность элементов одного типа, а
структура является совокупностью элементов произвольных
(практически) типов. Например:

struct address {
char name; // имя "Jim Dandy"
long number; // номер дома 61
char
street; // улица "South Street"
char town; // город "New Providence"
char
state[2]; // штат 'N' 'J'
int zip; // индекс 7974
};

Здесь определяется новый тип, называемый address, который задает
почтовый адрес. Определение не является достаточно общим, чтобы
учесть все случаи адресов, но оно вполне пригодно для примера. Обратите
внимание на точку с запятой в конце определения: это один из
немногих в С++ случаев, когда после фигурной скобки требуется
точка с запятой, поэтому про нее часто забывают.
Переменные типа address можно описывать точно так же, как и любые
другие переменные, а с помощью операции . (точка) можно обращаться
к отдельным членам структуры. Например:

address jd;
jd.name = "Jim Dandy";
jd.number = 61;

Инициализировать переменные типа struct можно так же, как массивы.
Например:

address jd = {
"Jim Dandy",
61, "South Street",
"New Providence", {'N','J'}, 7974
};

Но лучше для этих целей использовать конструктор ($$5.2.4). Отметим,
что jd.state нельзя инициализировать строкой "NJ". Ведь строки
оканчиваются нулевым символом '\0', значит в строке "NJ" три символа,
а это на один больше, чем помещается в jd.state.
К структурным объектам часто обращаются c помощью указателей,
используя операцию ->. Например:

void print_addr(address p)
{
cout << p->name << '\n'
<< p->number << ' ' << p->street << '\n'
<< p->town << '\n'
<< p->state[0] << p->state[1]
<< ' ' << p->zip << '\n';
}

Объекты структурного типа могут быть присвоены, переданы как фактические
параметры функций и возвращены функциями в качестве результата. Например:

address current;

address set_current(address next)
{
address prev = current;
current = next;
return prev;
}

Другие допустимые операции, например, такие, как сравнение (== и !=),
неопределены. Однако пользователь может сам определить эти операции
(см. главу 7).
Размер объекта структурного типа не обязательно равен сумме
размеров всех его членов. Это происходит по той причине, что
на многих машинах требуется размещать объекты определенных типов,
только выравнивая их по некоторой зависящей от системы адресации
границе (или просто потому, что работа при таком выравнивании будет
более эффективной ). Типичный пример - это выравнивание целого по
словной границе. В результате выравнивания могут появиться "дырки" в
структуре. Так, на уже упоминавшейся машине автора sizeof(address)
равно 24, а не 22, как можно было ожидать.
Следует также упомянуть, что тип можно использовать сразу после его
появления в описании, еще до того, как будет завершено все описание.
Например:

struct link{
link
previous;
link* successor;
};

Однако новые объекты типа структуры нельзя описать до тех пор, пока не
появится ее полное описание. Поэтому описание

struct no_good {
no_good member;
};
sageАноним 10/05/15 Вск 03:40:14 #243 №92628472 DELETED
является ошибочным (транслятор не в состоянии установить размер no_good).
Чтобы позволить двум (или более) структурным типам ссылаться друг на
друга, можно просто описать имя одного из них как имя некоторого
структурного типа. Например:

struct list; // будет определено позднее

struct link {
link pre;
link
suc;
list member_of;
};

struct list {
link
head;
};

Если бы не было первого описания list, описание члена link привело бы к
синтаксической ошибке.
Можно также использовать имя структурного типа еще до того, как тип будет
определен, если только это использование не предполагает знания размера
структуры. Например:

class S; // 'S' - имя некоторого типа

extern S a;

S f();

void g(S);

Но приведенные описания можно использовать лишь после того, как тип S
будет определен:

void h()
{
S a; // ошибка: S - неописано
f(); // ошибка: S - неописано
g(a); // ошибка: S - неописано
}

2.3.9 Эквивалентность типов



Два структурных типа считаются различными даже тогда, когда они имеют
одни и те же члены. Например, ниже определены различные типы:

struct s1 { int a; };
struct s2 { int a; };

В результате имеем:

s1 x;
s2 y = x; // ошибка: несоответствие типов

Кроме того, структурные типы отличаются от основных типов, поэтому
получим:

s1 x;
int i = x; // ошибка: несоответствие типов

Есть, однако, возможность, не определяя новый тип, задать новое имя
для типа. В описании, начинающемся служебным словом typedef, описывается
не переменная указанного типа, а вводится новое имя для типа.
Приведем пример:

typedef char Pchar;
Pchar p1, p2;
char
p3 = p1;

Это просто удобное средство сокращения записи.
sageАноним 10/05/15 Вск 03:40:37 #244 №92628490 DELETED
Ссылку можно рассматривать как еще одно имя объекта.
В основном ссылки используются для задания параметров и возвращаемых
функциями значений , а также для перегрузки операций (см.$$7).
Запись X& обозначает ссылку на X. Например:

int i = 1;
int& r = i; // r и i ссылаются на одно и то же целое
int x = r; // x = 1
r = 2; // i = 2;

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

int ii = 0;
int& rr = ii;
rr++; // ii увеличивается на 1

Здесь операция ++ допустима, но rr++ не увеличивает саму
ссылку rr; вместо этого ++ применяется к целому, т.е. к переменной ii.
Следовательно, после инициализации значение ссылки не может быть
изменено: она всегда указывает на тот объект, к которому была привязана
при ее инициализации. Чтобы получить указатель на объект,
обозначаемый ссылкой rr, можно написать &rr.
Очевидной реализацией ссылки может служить постоянный указатель,
который используется только для косвенного обращения. Тогда инициализация
ссылки будет тривиальной, если в качестве инициализатора указан адрес
(т.е. объект, адрес которого можно получить; см. $$R.3.7).
Инициализатор для типа T должен быть адресом. Однако, инициализатор
для &T может быть и не адресом, и даже не типом T. В таких случаях
делается следующее:
[1] во-первых, если необходимо, применяется преобразование типа
(см.$$R.8.4.3);
[2] затем получившееся значение помещается во временную переменную;
[3] наконец, адрес этой переменной используется в качестве инициализатора
ссылки.
Пусть имеются описания:

double& dr = 1; // ошибка: нужен адрес
const double& cdr = 1; // нормально

Это интерпретируется так:

double cdrp; // ссылка, представленная как указатель
double temp;
temp = double(1);
cdrp = &temp;

Ссылки на переменные и ссылки на константы различаются по следующей
причине: в первом случае создание временной переменной чревато
ошибками, поскольку присваивание этой переменной означает присваивание
временной переменной, которая могла к этому моменту исчезнуть.
Естественно, что во втором случае подобных проблем не существует.
и ссылки на константы часто используются как параметры функций
(см.$$R.6.3).
Ссылка может использоваться для функции, которая изменяет значение своего
параметра. Например:

void incr(int& aa) { aa++; }

void f()
{
int x = 1;
incr(x); // x = 2
}

По определению передача параметров имеет ту же семантику, что и
инициализация, поэтому при вызове функции incr ее параметр aa
становится другим именем для x. Лучше, однако, избегать изменяющих
свои параметры функций, чтобы не запутывать программу. В большинстве
случаев предпочтительнее, чтобы функция возвращала результат явным
образом, или чтобы использовался параметр типа указателя:

int next(int p) { return p+1; }
void inc(int
p) { (p)++; }

void g()
{
int x = 1;
x = next(x); // x = 2
inc(&x); // x = 3
}

Кроме перечисленного, с помощью ссылок можно определить функции,
используемые как в правой, так и в левой частях присваивания.
Наиболее интересное применение это обычно находит при определении
нетривиальных пользовательских типов. В качестве примера определим
простой ассоциативный массив. Начнем с определения структуры
pair:

struct pair {
char
name; // строка
int val; // целое
};

Идея заключается в том, что со строкой связывается некоторое целое значение.
Нетрудно написать функцию поиска find(), которая работает со структурой
данных, представляющей ассоциативный массив. В нем для каждой отличной от
других строки содержится структура pair (пара: строка и значение ). В
данном примере - это просто массив. Чтобы сократить пример, используется
предельно простой, хотя и неэффективный алгоритм:

const int large = 1024;
static pair vec[large+1];

pair find(const char p)
/
// работает со множеством пар "pair":
// ищет p, если находит, возвращает его "pair",
// в противном случае возвращает неиспользованную "pair"
/
{
for (int i=0; vec.name; i++)
if (strcmp(p,vec.name)==0) return &vec;

if (i == large) return &vec[large-1];

return &vec;
}

Эту функцию использует функция value(), которая реализует массив целых,
индексируемый строками (хотя привычнее строки индексировать целыми):

int& value(const char p)
{
pair
res = find(p);
if (res->name == 0) { // до сих пор строка не встречалась,
// значит надо инициализировать
res->name = new char[strlen(p)+1];
strcpy(res->name,p);
res->val = 0; // начальное значение равно 0
}
return res->val;
}

Для заданного параметра (строки) value() находит объект,
представляющий целое (а не просто значение соответствующего целого) и
возвращает ссылку на него. Эти функции можно использовать, например, так:

const int MAX = 256; // больше длины самого длинного слова

main()
// подсчитывает частоту слов во входном потоке
{
char buf[MAX];

while (cin>>buf) value(buf)++;

for (int i=0; vec.name; i++)
cout << vec.name << ": " << vec .val<< '\n';
}

В цикле while из стандартного входного потока cin читается по одному
слову и записывается в буфер buf (см. глава 10), при этом каждый
раз значение счетчика, связанного со считываемой строкой, увеличивается.
Счетчик отыскивается в ассоциативном массиве vec с помощью функции
find(). В цикле for печатается получившаяся таблица различных слов из cin
вместе с их частотой. Имея входной поток

aa bb bb aa aa bb aa aa

программа выдает:

aa: 5
bb: 3

С помощью шаблонного класса и перегруженной операции [] ($$8.8)
достаточно просто довести массив из этого примера до настоящего
ассоциативного массива.
sageАноним 10/05/15 Вск 03:42:26 #245 №92628558 DELETED
В С++ можно задавать значения всех основных типов:
символьные константы, целые константы и константы с плавающей точкой.
Кроме того, нуль (0) можно использовать как значение указателя
произвольного типа, а символьные строки являются константами типа
char[]. Есть возможность определить символические константы.
Символическая константа - это имя, значение которого в его области
видимости изменять нельзя. В С++ символические константы можно задать
тремя способами: (1) добавив служебное слово const в определении,
можно связать с именем любое значение произвольного типа;
(2) множество целых констант можно определить как перечисление;
(3) константой является имя массива или функции.

2.4.1 Целые константы



Целые константы могут появляться в четырех обличьях: десятичные,
восьмеричные, шестнадцатеричные и символьные константы. Десятичные
константы используются чаще всего и выглядят естественно:

0 1234 976 12345678901234567890

Десятичная константа имеет тип int, если она умещается в память,
отводимую для int, в противном случае ее тип long. Транслятор должен
предупреждать о константах, величина которых превышает выбранный формат
представления чисел.
Константа, начинающаяся с нуля, за которым следует x (0x), является
шестнадцатеричным числом (с основанием 16), а константа, которая
начинающаяся с нуля, за которым следует цифра, является восьмеричным
числом (с основанием 8). Приведем примеры восьмеричных констант:

0 02 077 0123

Их десятичные эквиваленты равны соответственно: 0, 2, 63, 83.
В шестнадцатеричной записи эти константы выглядят так:

0x0 0x2 0x3f 0x53

Буквы a, b, c, d, e и f или эквивалентные им заглавные буквы
используются для представления чисел 10, 11, 12, 13, 14 и 15,
соответственно. Восьмеричная и шестнадцатеричная формы записи наиболее
подходят для задания набора разрядов, а
использование их для обычных чисел может дать неожиданный эффект.
Например, на машине, в которой int представляется как 16-разрядное
число в дополнительном коде, 0xffff есть отрицательное десятичное
число -1. Если бы для представления целого использовалось большее число
разрядов, то это было бы числом 65535.
Окончание U может использоваться для явного задания констант типа
unsigned. Аналогично, окончание L явно задает константу типа long.
Например:

void f(int);
void f(unsigned int);
void f(long int);

void g()
{
f(3); // вызов f(int)
f(3U); // вызов f(unsigned int)
f(3L); // вызов f(long int)
}
Аноним 10/05/15 Вск 04:11:43 #246 №92629361 
опа
Аноним 10/05/15 Вск 04:17:44 #247 №92629549 
>>92626911
Ну так и как же без всяких костылей блджад?
Аноним 10/05/15 Вск 09:35:37 #248 №92635817 
14312397376110.jpg
14312397376211.jpg
Аноним 10/05/15 Вск 09:39:15 #249 №92635932 
Пилоты гнойные. Привстал
Аноним 10/05/15 Вск 09:46:36 #250 №92636175 
14312403969060.jpg
14312403969141.jpg
14312403969262.jpg
Аноним 10/05/15 Вск 09:50:23 #251 №92636337 
вы лучше посоветуйте качественных мамоебских тайтлов с сюжетом. где гг не ебаный шота а более менее взрослый а то на "синфул мозер" и "табое хаха" забеался уже дрочить
Аноним 10/05/15 Вск 11:34:03 #252 №92640252 
14312468431840.png
Аноним 10/05/15 Вск 11:50:29 #253 №92641079 
>>92623291
>2015
>регистрация
>предлагают ставить какие-то мутные левые костыли на браузер
Аноним 10/05/15 Вск 12:25:31 #254 №92643180 
>>92641079
Я ничего не предлагал, мне похуй вообще.
Аноним 10/05/15 Вск 13:09:44 #255 №92645972 
14312525847340.png
https://hitomi.la/character/z1-all-1.html И не ебите мозги.
Аноним 10/05/15 Вск 14:45:30 #256 №92652078 
>>92624389
лол
Аноним 10/05/15 Вск 15:31:21 #257 №92655032 
14312610818700.jpg
comments powered by Disqus

Отзывы и предложения