Наредби во C++
Оваа статија можеби бара дополнително внимание за да ги исполни стандардите за квалитет на Википедија. Ве молиме подобрете ја оваа статија ако можете. |
1. Дали декларирањето на параметарот и / или повратната вредност како const помага компајлерот да генерира повеќе оптимален код или на друг начин го подобри својот кодот генерација?
Она што би можело компајлерот да го направи подобро? Дали би можело да се избегне копија на параметар или враќање вредност? Не, бидејќи параметарот веќе е усвоен од страна на референца, а враќањето е веќе со повикување.Дали би можело да се стави копија од x или некое Y во read-only меморија? Не, бидејќи и двете x и некое Y живеат надвор од нејзиниот опсег и доаѓаат од и / или се дадени на надворешниот свет. Дури и ако некое Y е динамички доделено во движење во рамките f себе, таа и нејзините сопственост се дадени до повикувачот.
Но, што за можните оптимизации на кодот, кој се појавува во внатрешноста на телото на f? Поради const, дали може компајлерот некако да го подобри кодот кој го генерира за телото на f? Ова води во второто и повеќе општо прашање:
3.Под кои околности и за какви видови класа Z може овој const квалификација може да генерира различен и подобар код? Разговараат. Ако компајлерот знае дека Z вистински е const објект, тие можат да вршат некоја корисна оптимизација дури и без глобални анализи. Пример, ако телото на fсодржи повик како g (& Z), компајлерот може да биде сигурен дека не-променливи делови на z не се менуваат во текот на повик за g. Освен тоа, сепак, пишување const во Пример 24-3 не е оптимизација за повеќето класи Zand во оние случаи каде што тоа е оптимизација, тоа не е компајлер кодот генерациска оптимизација
За компајлер кодот генерација, прашањето главно се сведува на тоа дали компајлер може Elide копија изградба или може да се стави z во read-only меморија. Тоа е, тоа би било убаво ако знаевме дека z бил навистина недопрен од она што се случува внатре f, бидејќи теоретски тоа би значело ние би можеле да бидеме во можност само директно да користат објект од страна кој повикувачот премина во оваа функција, наместо за правење копија од него, или од друго место би можеле да се направи копија но да се стави таа копија во read-only меморија, ако тоа се случува да биде побрзо или на друг начин повеќе пожелно. Во принцип, компајлерот не може да користи constness параметар за Elide копија изградба или претпостави битово побарување. Како што е веќе забележано, многу нешта можат да тргнат наопаку. Особено Z би можеле да има променливи членови, или некој некаде (во F себе, во некои други функција, или во некои директно или индиректно наречен Z член функција) може да се изврши const_casts или други измами. Постои еден случај каде што на компајлерот може да биде во состојба да генерира подобар код. ако:
● дефинициите за копија конструктор на Z и за сите функции на Z кои се користат директно или индиректно во телото на функцијата f се видливи во овој момент, и ● тие функции се прилично едноставни и без несакани ефекти и
● на компајлерот има агресивна оптимизатор
тогаш компајлер може да биде сигурен за тоа што точно се случува на секој чекор од патот, и може да избере да Elide копија под "како да" правило, во која се вели дека компајлер е дозволено да направи нешто различно од она што стандардот технички вели тоа мора сè додека програмата сообразени не може да ја каже разликата. Како настрана, тоа вреди да се спомене една мала црвена херинга: Некои луѓе може да се каже има друг случај каде што компајлерот може да генерира подобар код со const, имено, ако компајлер врши глобална оптимизација. Работата е дека оваа реченица е точна дури и ако се отстрани "со const." Никогаш не заборавајте дека глобалната оптимизација сè уште е прилично ретка и многу скапа, вистинскиот проблем тука е тоа што глобалната оптимизација го прави користењето на сите знаења како објект е всушност користен и тоа ќе го прават истото дали објектот е всушност прогласен const или не тоа донесува одлуки врз основа на она што навистина го прават, а не на она што го вети да прават, па тоа не е важно ако двете се случи да биде исто. Ако сте добивале вистинска глобален оптимизација, сепак, тогаш ветува constness дефинитивно не им помагаат на оптимизаторот да ја работи својата работа подобро во тој поглед. Имајте на ум дека неколку параграфи претходни реков дека "пишување const во Пример 24-3 не е оптимизација за повеќето класи Z" и "за компајлер кодот генерација." Има, меѓутоа, повеќе оптимизации се можни отколку што се сонуваше во оптимизатор вашиот компајлер! И навистина const може да им овозможи на некои вистински оптимизации: За видови на околностите наведени во (а), зборуваме за компајлерот оптимизација или некој друг вид на оптимизација? Објасни. Особено, постојат и програмски оптимизации, каде што авторот на Z интелигентно може да избере да се прават работите на поинаков начин за const објекти. Како што е случај во точка, да речеме дека Пример 24-3 е Z е се справи / тело класа, како што е класата String кој користи референца броење да се изврши мрзливо копирање: // Example 24-4 // void f(const String s) {
// …
s[4]; // or use iterators
// …
} Како стринг, знаејќи дека повикувачкиот оператор [] на const Стринг не треба да бидат способни да се промени содржината на стрингот, може да одлучи да обезбеди const преоптоварување за оператор [], која враќа знак од вредност наместо знак class String {
// ...
public:
const char operator[](size_t) const; char& operator[](size_t);
// ...
} Слично на тоа, стрингот, исто така, може да обезбеди const_iterators чиј оператор * враќа знак од вредност наместо знак &. Ако Стринг оваа работа, и ако се случи да го користите smartened-up оператор [] или iterators, а вие го прогласи премин од вредност параметар како const, thenwonder на чудата! Стринг може, без дополнителна помош од вас, автоматска и целото somely оптимизирате вашиот код, преку избегнување на длабоко копија ... Које подобар начин да се добие истиот ефект? … but you get all this and more by simply writing pass-by-reference instead: // Example 24-5: Just do it better than Example 24-3 // void f(const Z& z) {
// …
} Краток преглед Тоа е заедничко верување дека const точноста помага компајлерите да произведуваат построг код. Да, const е навистина добра работа, но поентата на оваа точка е дека const е главно за луѓето, наместо за компајлери и оптимизери. Кога станува збор за пишување безбеден кодот, const е одлична алатка која ви овозможува програмерите да пишуваат побезбеден код со компајлер проверка и спроведување. Дури и кога станува збор за оптимизација, const сè уште е главно корисен како алатка која ви овозможува човечка класа дизајнери подобро спроведување рачно изработени оптимизации и помалку како ознака за сезнаен компајлер автоматски да се генерира подобар код.
PART 7 chapter 39 Во оваа точка, ќе видиме дека сите следниве функции може да се спроведе како не-членка nonfriends: resize (2) assign (6) += (3) append (6) push_back insert (7 all but the three-parameter version) Let's investigate. resize Can string::resize be a nonmember function? Explain. Well, let's see: void resize(size_type n, charT c); void resize(size_type n);
Секоја промена на големината може да биде nonmember nonfriend? Секако тоа може, бидејќи тоа може да се спроведе во однос на јавниот интерфејс basic_string без губење на ефикасноста. Всушност, сопственик на стандардот функционални спецификации изразат двете верзии во однос на функциите што веќе се разгледуваат. На пример: template<class charT, class traits, class Allocator> void resize(basic_string<charT, traits, Allocator>& s,
typename Allocator::size_type n, charT c)
{
if(n > s.max_size()) throw length_error("won't fit"); if(n <= s.size()) { basic_string<charT, traits, Allocator> temp(s, 0, n); s.swap(temp);
} else {
s.append(n - s.size(), c); }
}
template<class charT, class traits, class Allocator> void resize(basic_string<charT, traits, Allocator>& s,
typename Allocator::size_type n)
{
resize(s, n, charT());
Тоа е еден начин да ги спроведат како nonmember nonfriends кои се исто толку ефикасни како членови. Анализирај ги следните член функции на std :: стринг и да покажи дали тие може или што треба да бидат nonmember функции. Оправдај ги вашите одговори. assign and +=/append/push_back Од оваа група, ајде прво се справи доделите: Имаме emsix форми sixcount "на доделите. За среќа, овој случај е едноставен: Повеќето од нив се веќе утврдени во однос на едни со други, и сите може да се спроведе во однос на конструкторот и операторот = комбинација. Следно, оператор + =, append и push_back: Што е со сите оние досадени вкусови на додавање операции, имено три форми на оператор + =, six цcount 'em six форми на "на append и сам push_back? Само сличноста треба да ни алармира на фактот дека веројатно најмногу e потребно е да биде член. По сите, тие се прават за една иста работа, дури и ако детали разликува, на пример, додавање на лик во еден случај, низа во друг случај, и iterator опсег во уште еден случај. Всушност, како што излезе, сите нив, исто така, може да се спроведе како nonmember nonfriends без загуба на ефикасност: ●Јасно оператор + = може да се спроведе во однос на append, бидејќи тоа е како што е наведено во C + + стандардната. ●Подеднакво јасно, пет од шест верзии на append може да биде nonmember не-пријатели, бидејќи тие се специфицирани во однос на три-параметар верзија на append, и дека за возврат може да се спроведе во однос на вметнете, од кои сите сосема го затвора append семејството ●Утврдувањето на статусот на push_back зема само малку повеќе работа, бидејќи нејзините оперативните семантики не се наведени во basic_string клаузула но само во контејнер барањата клаузула, и таму наоѓаме спецификација која a.push_back (x) е само синоним за . вметнете (a.end (), x).. "Но, чекај!" некој може да каже, "C + + стандардната вели дека доделувањето оператори мора бидат членови, и + = е оператори за доделување!" Да и не. Без навлегување во крвави детали, иако + = се наведени заедно со сите други * = оператори како доделен оператор во C + + јазикот граматика, само оператори за доделување дека мора да биде член , како што е веќе забележано, операторот = себе. Затоа, следниве примерок имплементации на операторот nonmember + = функции се совршено валидни C + +: template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator>& operator+=(basic_string<charT, traits, Allocator>& s,
const basic_string<charT, traits, Allocator>& t) { return s.append(t);
}
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator>& operator+=(basic_string<charT, traits, Allocator>& s, const charT* p) {
return s.append(p);
}
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator>& operator+=(basic_string<charT, traits, Allocator>& s, charT c) {
return s.append(1, c);
} Што е важниот дел којшто држи сите членови на оваа група заедно како валидна кои не се членки nonfriends? Тоа insert, па оттука статусот insert е добар избор за да се биде член кој ја врши работата и encapsulates на append поврзани со пристап до internals низа во едно место, наместо ширење на внатрешен пристап насекаде во десетина различни функции. vinsert
Потоа, внесете: За оние од вас кои мислат дека шест-count'em и шест верзии како-знак и шест count'em и шест верзии на append би биле малку многу, тие беа само warmup. Сега сметаме дека осум count'em и осум верзии на внесување. Јас веќе номинирав три-параметар верзија на insert како член, а сега е време да се оправда зошто. Прво, како што е наведено претходно, insert е една поопшта операција отколку append и откако членot INSERT ги дозволува сите append операции да бидат не-членка nonfriends, ако ние немаме барем еден INSERT како член, тогаш барем еден APPENDS би требало да биде член, па одбрав да номинираM повеќе основнa и флексибилa операција. Но ние имаме осум count'em и осум верзии на insert. Од Кои една (или повеќе) треба да биде член (ови)? Пет од осум форми на insert се веќе утврдени во однос на три-параметар форма, а други, исто така може да се спроведат ефикасно во услови на три-параметар форма, па нас ни треба една форма да биде член. Другите можат да бидат nonmember nonfriends.
Операции кои мора да бидат членови
Кои членски функции од std::string мора да бидат членски функции? Зошто? Како прв чекор, да ги одвоиме оние операции кои едноставно мора да бидат членски функции. На почетокот на листата има неколку кои се очигледни. Конструктори (6) Деструктор Оператори Оператори за задача (3) [] оператори (2) Воие оператори мора да бидат членови. Невозможно е да се напише конструктор, деструктор, оператор за задача или [] оператор на било кој друг начин. Операции кои мора да бидат членови Кои членски функции од std::string треба да бидат членови? Зошто? На кои операции им треба пристап до внатрешните податоцикои инаку треба да ги дадеме преку пријателство? Јасно е дека овие имаат причина да бидат членови и нормално треба да бидат. Оваа листа ги вклучува следните, од кои некои обезбедуваат индиректен пристап до (на пример begin) или ја менуваат (на пример reserve)внатрешната состојба на низата: Овие треба да бидат членови не само затоа што тие се цврсто поврзани со basic_string (основната низа) туку исто така бидејќи го формираат јавниот интерфејс кој нечленските и непријетелските функции треба да го користат. Секако, овие можете да ги примените како нечленски функции, но зошто? (всушност има причина која можеби ќе ја претпочитате з ада напишете нечленски непријателски верзии исто такакои едноставно преминуваат кон членовите, имено униформност на интерфејсот; ова повторно ќе се дискутира понатаму) На прашањето за insert, erase, и replace ќе се навратиме малку подоцна. Особено за replace важно е да можете добро да одберете и да ја направите најфлексибилната фундаментална верзиај (верзии)во членот (членовите). Во акција: Можно-спорни операции кои можат да бидат нечленски непријателски - Прво во овој дел, дозволете ми да направам импресивна имитација на громобран истакнувајќи дека сите од следниве функции имаат нешто фундаментално заедничко, имено: секоја може очигледно лесно и делотворно да биде нечленска непријателска. - Се разбира дека можат да бидат нечленски непријателски; тоа е очигледно, лесно. Се разбира дека овие функции исто така понекогаш можат да имаат и нешто друго прилично фундаментално заедничко: тие се споменати во контејнерот на библиотеката на стандарди и низата на барања како членски функции. - земете ја empty како пример. Дали можеме да ја имплементираме како нечленска непријателска? Секако ...самиот стандард го бара следното однесување на basic_string::empty, - Па, сега, прилично е лесно да се запише како нечленска без да се изгуби ефикасност: - Се разбира, ако претходно немавме големина, имплементацијата на empty како нечленска непријателска немаше а биде возможно. Алтернативно (и со во некои случаи посигурна изведба) би можеле да ја имплементираме како: Колку посебни грешки траба да се пријават кога се компајлира следниот код на согласен (соодветен) Ц++ компајлер? Краткиот одговор е нула. Овој код е совршено легален и во согласност со стандардите (без разлика дали неговиот автор го сакал тоа или не). Да ги разгледаме еден по еден сите изрази кои можат да бидат сомнителни и да видиме зошто тие навистина се во ред: - 0[p]е легален и има точно исто значење со p[0]. Во Ц (и Ц++), изразот на формата x[y], каде еден од x и y е тип на покажувач а другиот е целобројна вредност, секогаш значи *(x+y). Во овој случај, 0[p] и p[0] имаат исто значење бидејќи тие значат *(0+p) и *(p+0), поединечно, што води до истата работа.
- and и not се валидни клучни зборовикои претсавуваат алтернативни спелувања на && и !, соодветно.
- :> е легален. Тој е диграф за карактерот] , не е смајли (смајлите не се поддржани во Ц++ јазикот надвор од блоковите за коментирање што е прилично за срамота). Ова го претвора завршниот дел од изразот во p[1]>p[2].
- дополнителната точка запирка на крајот од декларацијата за членската функција е дозволена и целосно доброќудна (неопасна). Синтаксата за дефиниција на класа во Ц++ дозволува декларација на празен член (задебелена точка запирка ) да се појави било каде, онолку често колку што сакате. На пример, следнава е совршено легална дефиниција з акласа без членови: X { ;;;;;;;;;; };