Правило 27, не злоупотребявайте с тип реализация - ефективно използване на в
Правило 27: Не прекалявайте тип реализация
C ++ правила са проектирани да работят правилно с видове беше невъзможно. На теория, ако вашата програма компилира без грешки, това означава, че тя не се опитва да изпълнява всякакви опасни операции или безсмислени обекти. Това е ценен гаранция. Няма нужда да го изоставят.
За съжаление, шофиране видове байпас система. И това може да доведе до различни проблеми, някои от които са лесни за разпознаване, а някои - е изключително трудно. Ако идвате с C ++ от света на C, Java, C # или, калиев етоксид да вземат предвид, тъй като в тези езици в посочените по-горе видове често има необходимост, и те са по-малко опасно, отколкото в C ++. Но C ++ - това не е C. Това не е Java. Това не е C #. В привеждане на този език - това означава, че искате да бъдат третирани с уважение.
Нека започнем с видовете от проучването на гласове оператори синтактични, защото има три различни начина да пишат едно и също нещо. Намаляване на C-стил изглежда така:
(Т) експресия // изразяват тип Т
Функционално синтаксис шофиране е:
T (експресията) // изразяват тип Т
Между тези две форми не съществува значителна разлика, просто скоби са подредени по различен начин. Аз наричам тези форми на призраци в стар стил.
C ++ също въвежда четири нови форми на отливки (често наричани призраци в стила на C ++):
Всеки от тях има свой собствен смисъл:
• const_cast обикновено се използва, за да се отхвърли постоянен обект. Никой друг актьорски състав в C ++ стил не позволява това;
• dynamic_cast се използва главно за извършване на "безопасна Даункастинг» (Даункастинг). Това позволява на оператора да определи дали обектът принадлежи към наследяване йерархия даден тип. Това е единственият шофиране, което не може да се извърши с помощта на стария синтаксис. Това е и единственият притегляне, което може да изисква значително разходите по време на изпълнение (по-подробно по-късно);
• reinterpret_cast предназначена за призраци от ниско ниво, които генерират по изпълнението зависими (т.е. не-преносими) резултати, като например привеждане показалеца Int. Отвъд кода на ниско ниво, като това намаление трябва да се използва рядко. Аз го използва в тази книга само веднъж, когато се обсъждат писането на отстраняване на грешки разпределител (виж правило 50.);
• static_cast може да се използва за конвертиране на типове изрични (например, до не-конст обекти константа (както в правило 3), Int да се удвои и т. П.). Тя може да се използва за извършване на обратната трансформация (например, невалидни * указатели към въведен указатели, указатели към базовия клас на получени показалеца). Но донесе постоянен обект на не-константа, този оператор не може (това наследство const_cast).
Прилагане на намаления в стария стил е напълно законно, но новата форма е за предпочитане. Първо, те са много по-лесно да намерите кода (за хората, така и за инструмент като Впиши), който опростява процеса на търсене в кода на местата, където системата за писане е в опасност. На второ място, по-тясно специализирана цел на всеки оператор на гласове позволява компилатори за диагностика техните грешки на употреба. Например, ако се опитате да се отървете от постоянството използва всеки оператор гласове в C ++ стил, с изключение const_cast, тогава кода ви не компилирате.
Аз използвам един стар стил хвърля само, когато искам да се обадя изричното конструктора да премине един обект като параметър на функцията. Например:
изрично приспособление (размер на междинно съединение);
нищожен doSomeWork (Конст Widget w);
doSomeWork (приспособление (15)); // създаде приспособление от междинно съединение
// функция довеждане
doSomeWork (static_cast (15)); // създаде приспособление от междинно съединение
// за привеждане на C ++ стил
Но умишлено създаване на един обект не се "чувстват" като гласове, така че в този случай е вероятно по-добре да се използва функционално намаляване вместо static_cast. Както и да е, кодът, който води към катастрофа, обикновено изглежда доста разумно, когато пишете, така че е най-добре да не обръща внимание на чувствата и винаги използвайте призраците в новия стил.
Много програмисти смятат, че актьорите просто казва на компилатора, че един вид трябва да се третира като приятел, но те са наред. Вид конвертиране на всякакъв вид (както изрично от шофирането и подразбиращ се извършва от компилатора) често водят до появата на кода, изпълнен по време на изпълнение. Разгледаме следния пример:
двойно г = static_cast (х) / у; // х участък за използване у
// плаващия разделение точка
Привеждане INT х да се удвои почти със сигурност генерира изпълним код, тъй като в повечето архитектури вътрешния представителството на едно цяло число е различно от двойно представителство. Ако това е, че не правя особено изненадан, но погледнете следния пример:
клас Изчислено: публична база;
База * PB = г; // имплицитно преобразуване Получава *
Ето, ние току-що създадената база клас указател към обект, получен, но понякога точката на две насоки не е на същото. В този случай, по време на изпълнение на показалеца Извлечен * добавя офсет, за да получите точната стойност на база * показалеца на.
Интересното за призраци, - в това, че е лесно да се напише код, който изглежда правилното (и може да бъде вярна на други езици), но всъщност не е вярно. Например, много рамки за разработка на приложения изисква функциите на виртуални страни, определени в новия клас, първо се обаждат на съответната функция на базовия клас. Да предположим, че имаме базов клас прозорец и клас, произведен от него SpecialWindow и в двете виртуална функция дефинирана onResize. По-нататък се предположи, че onResize на SpecialWindow ще доведе до първия onResize от прозорец. Следваща изпълнение изглежда добре, но в действителност погрешно:
клас Window / базовый класс
виртуален невалидни onResize () // изпълнение в основата onResize
клас SpecialWindow: обществен Window / производный класс
виртуален невалидни onResize () / реализация onResize
static_cast (* тази) .onResize (); // в извлечен клас;
// отливка * това да Window,
// след това извиква onResize му;
// това не работи!
// изпълнение специфични
> // SpecialWindow част onResize
Аз се подчертава в този код гласове. (Това е да се съберат в един нов стил, но се използва стар стил, не променя нищо.) Както се очакваше, * това води до типа на прозореца. Затова се обръщам към onResize резултати в Window повикване :: onResize. Това е само тази функция няма да бъде ползвана за текущия обект! Неочаквано, нали? Вместо това, операторът на гласове ще създаде нов, временно копие на базовия клас * това и да причини onResize за това копие! В горния код няма да доведе до Window :: onResize за текущия обект и след това при извършването на конкретна SpecialWindow действие - той отговаря на прозореца :: onResize за копие от базовия клас на текущия обект, преди да извършвате специфични SpecialWindow действие за обекта. Ако Window :: onResize променя предмета (което е напълно възможно, тъй като onResize - не постоянно член функция), текущия обект няма да бъдат променени. Вместо това, той се променя копие на този обект. Въпреки това, ако SpecialWindow :: onResize променя даден обект, той ще бъде променен, е текущият обект. В резултат на това текущия обект е в състояние на несъвместимост, тъй като изменение на частта, която принадлежи на базовия клас, няма да се изпълни, и изменение на част принадлежност към новия клас ще бъде.
Решението е да се премахнат от актьорите, заменяйки я с това, което наистина е имал предвид. Не е необходимо да се извършват никакви трикове с компилатор, карайки го да се тълкува това като * обект базов клас. Вие искате да се обадите базов клас версия на onResize за текущо обекта. Така се процедира, както следва:
клас SpecialWindow: обществен Window
виртуален невалидни onResize ()
Window :: onResize (); // повикване Window :: onResize на * това
Горният пример показва също така, че веднага след като се чувстват желание да се направи кастинг, това е знак, че може да сте на грешен път. Това е особено вярно dynamic_cast оператор.
Преди да навлезем в детайлите на dynamic_cast, заслужава да се отбележи, че повечето реализации на работа на оператора е доста бавен. По този начин, най-малко един от настоящите приложения на класове на базата на имена сравнителни представена струни. Ако се прави dynamic_cast обект клас, принадлежащ на йерархия с дълбочина на единствената наследство на четири нива, като всяко обаждане до dynamic_cast в такова изпълнение може да ви струва четири покани strcmp за сравнение имената на класовете. За по-дълбока йерархия или такава, в която има множество наследство, тази операция ще бъде още по-скъпо. Има причини, поради които някои приложения да работят по този начин (защото те трябва да подкрепят динамично свързване). По този начин, в допълнение към бдителност по отношение на типа леене, по принцип, трябва да сте особено скептичен, когато става въпрос за използването на dynamic_cast в програмата, чието изпълнение е на първо място.
Произход - използвайте контейнери за съхранение на показалеца (често "интелигентна", обикновено 13 cm.) На самите предмети, извлечени класове, като се избягва необходимостта да се манипулират тези обекти чрез база клас интерфейси. Например, ако само SpecialWindow подкрепя трептене (мига) в йерархията на нашата Window / SpecialWindow, вместо това:
typedef // види. член 13,
STD :: вектор> VPW; // за TR1 :: shared_ptr