Правило 20, предпочитам преминаване чрез позоваване на Конст преминаване по стойност - ефективно

Правило 20: Предпочитам да преминава чрез позоваване на Конст преминаване по стойност

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

Лице (); // параметри са пропуснати за простота

Лице (); // виж Правило 7 -. Защо виртуална

клас Student: публична личност

Student (); // параметри са пропуснати тук

Сега погледнете следния код, който се нарича validateStudent функция, която приема аргумент Student (по стойност) и се връща знака на неговата правилност:

BOOL validateStudent (Student и); // функция се

// Студентски по стойност

Студентски Платон; // Платон научил от Сократ

BOOL platoIsOk = validateStudent (плато); // извикаме функцията

Какво се случва, когато се обадите на тази функция?

Ясно е, че строителят на Студентски копие се нарича да се инициализира параметърът плато. Също така е ясно, че S унищожени, когато се връщаше от валидира-студент. Ето защо, параметъра предаване по стойност на тази функция струва в един разговор Студентски копие конструктор и Студентски деструктор повикване.

Но това не е всичко. Студентски обект съдържа в себе си два обекта: низ, така че всеки път, когато се изгради Студентски обект, който също трябва да изгради и тези два обекта. Студентски клас наследява клас лице, така че всеки път, проектиране Студентски обект, трябва да се проектират и Person обект. Но Лице обект съдържа две низ обект, така че всеки човек с изграждане е свързано с две повиквания низ конструктор. По този начин трансферът на Студентски обект по стойност, води до едно обаждане Студентски копие конструктор, копирайте конструктор повикване една Личност и четири покани низови копиране конструкторите. Когато копие от студентска обектът се унищожава, всеки конструктор повикване съответства деструктор повикване, така че общата стойност на Студентски пас на стойност не е шест конструктори и деструктори шест!

Е, това е правилното и желаното поведение. В края на деня, искате всички ваши предмети са правилно инициализират и унищожени. И все пак би било хубаво да се намери начин да пропуснете всички тези разговори конструктори и деструктори. Има начин! Тя - преминаване чрез позоваване на постоянна:

булев validateStudent (Конст Студентски и);

еталонен параметър също избягва "рязане" на проблема (нарязване). Когато получен клас обект се прехвърля (по стойност) като обект конструктор база клас е наречен копиране база клас, и тези части, които принадлежат производно "отрязани". Просто трябва просто базов клас обект - което е съвсем естествено, тъй като тя е създадена база клас конструктор. Това е почти винаги не това, което трябва. Да предположим, че сте работили с набор от класове за изпълнение на графична система прозорец:

име станд :: низ () Конст; // връща името на прозореца

виртуален невалидни дисплей () Конст; // привлече прозорец и съдържание

клас WindwoWithScrollBars: обществен Window

виртуален невалидни дисплей () Конст;

Всички клас Window обекти имат име, което можете да получите чрез името на функцията, както и всички прозорци могат да бъдат показани, както е посочено от наличието на функции на дисплея. Фактът, че на дисплея - виртуална функция, се казва, че начинът да се покаже прост клас Window обекти база може да се различава от начина на показване на обекти WindowWithScrollBar (виж правила 34 и 36.).

Сега да предположим, че искате да напишете функция, която ще отпечата името на прозореца и след това ще го покаже. Това е грешен начин да се напише такава функция:

нищожен printNameAndDisplay (Window w) // грешно! параметър

Нека да видим какво ще се случи, ако се обадите на тази функция, като подава обект WindowWithScrollBar:

w параметър ще бъде изграден - тя се предава по стойност, помниш ли? - като Window обект, както и цялата допълнителна информация, което го прави обект WindowWithScrollBar, ще бъдат отрязани. Вътре printNameAndDisplay w винаги ще се държи като обект на клас Window (защото тя е обект на клас Window), независимо от вида на обекта, всъщност се прехвърля на функцията. По-специално, функцията за показване на повикване в рамките printNameAndDisplay винаги предизвика Window :: дисплей, никога - WindowWithScrollBar :: дисплей.

Отстраняване "рязане" - w подаване чрез позоваване на постоянна:

нищожен printNameAndDisplay (Конст Window w) // правилния вариант

Сега w се държи правилно, без значение какво може да представлява прозорец към реалността.

Ако погледнете "под капака» C ++, ще видите, че референции обикновено са реализирани като указатели, така че прехвърлянето на нещо чрез позоваване обикновено означава показалеца на предаване. В резултат на вградените обекти от тип (например, Int) е винаги по-ефективно предаване по значение от справка. Ето защо, за вградените типове, ако имате възможност за избор - да мине по стойност или чрез позоваване на константа, че има смисъл да се избере пас на стойност. Същото се отнася и за съветите итератори и функция обекти STL, защото те са специално предназначени за стойността на трансфери. Програмистите прилагане итератори и функционални обекти, са отговорни за, за да се гарантира ефективността предаване на тяхната стойност и да се изключи "Cut". Това е един пример за това как правилата се променят в зависимост от частта, за която C ++ (см. 1 най-общо).

Дори когато малки обекти са евтини в копиране конструктори, те все още могат да окажат влияние върху производителността. Някои компилатори лечение вградени и потребителски дефинирани типове по различни начини, дори ако те имат един и същ вътрешен представителство. Например, някои компилатори не поставяйте предмети, състоящи се от само една двойна в регистрите, дори и да има желание да поставите стойността вграден тип двойно. В такива случаи е по-добре да премине обекти чрез препратка, защото компилаторът със сигурност е готов да постави в показалеца на регистър (който осъществява връзка).

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

Най-общо казано, единствените видове, за които може да се предположи, че трансферът на стойност ще е евтина - тя е вградена видове, както и итератори и STL функционални обекти. За всичко останало, следвайте съветите на правилата и да премине параметри чрез позоваване на постоянна стойност, вместо на предаването.