Короткий запитальник по C + +. Частина 1 (FAQ)

Цей документ є перекладом збірки відповідей на часті питання групи новин comp.lang.c + +.


[8.1] Що таке посилання?


Посилання – це псевдонім (інше ім'я) для об'єкта.


Посилання часто використовуються для передачі параметра за посиланням:

    void swap(int& i, int& j)
{
int tmp = i;
i = j;
j = tmp;
}

int main()
{
int x, y;
// …
swap(x,y);
}


У цьому прикладі i і j – Псевдоніми для змінних x і y функції main . Іншими словами, i – Це x . Не покажчик на x і не копія x , А сам x . Все, що ви робите з i , Проробляється з x , І навпаки.


Ось таким чином ви як програміст повинні сприймати посилання. Тепер, ризикуючи дати вам невірне уявлення, кілька слів про те, який механізм роботи посилань. У основі посилання i на об'єкт x – Лежить, як правило, просто машинний адреса об'єкту x . Але коли ви пишете i++ , Компілятор генерує код, який инкрементируются x . Зокрема, сама адреса, який компілятор використовує, щоб знайти x , Залишається незмінним. Програміст на С може думати про це, як якщо б використовувалася передача параметра за вказівником, в дусі мови С, але, по-перше, & (Взяття адреси) було б з місця з викликає функції в спричинюється, і, по-друге, в викликається функції були б прибрані * (Разименованія). Іншими словами, програміст на С може думати про i як про макроозначень для (*p) , Де p – Це покажчик на x (Тобто, компілятор автоматично разименовивает підлягає покажчик: i++ замінюється на (*p)++ , А i = 7 на *p = 7 ).


Важливе зауваження: незважаючи на те що в якості посилання в остаточному машинному коді часто використовується адресу, не думайте про заслання просто як про забавно виглядає покажчику на об'єкт. Посилання – це об'єкт. Це не покажчик на об'єкт і не копія об'єкта. Це сам об'єкт.


[8.2] Що відбувається в результаті привласнення посиланням?


Ви міняєте стан засланця об'єкта (того, на який посилається посилання).


Пам'ятайте: посилання – це сам об'єкт, тому, змінюючи посилання, ви міняєте стан об'єкта, на який вона посилається. Мовою виробників компіляторів посилання – це lvalue (left value – значення, яке може з'явитися ліворуч від оператора присвоювання).


[8.3] Що відбувається, коли я повертаю з функції посилання?


У цьому випадку виклик функції може виявитися з лівого боку оператора (операції) присвоєння.


На перший погляд, такий запис може здатися дивною. Наприклад, запис f() = 7 виглядає безглуздою. Однак, якщо a – Це об'єкт класу Array , Для більшості людей запис a[i] = 7 є осмисленою, хоча a[i] – Це всього лише замаскований виклик функції Array::operator[](int) , Яка є оператором звернення за індексом для класу Array :

    class Array {
public:
int size() const;
float& operator[] (int index);
// …
};

int main()
{
Array a;
for (int i = 0; i < a.size(); ++i)
a [i] = 7; / / У цьому рядку викликається Array:: operator [] (int)
}


[8.4] Як можна перевстановити посилання, щоб вона посилалася на інший об'єкт?


Неможливо в принципі.


Неможливо відокремити посилання від її об'єкта.


На відміну від покажчика, посилання, як тільки вона прив'язана до об'єкта, не може бути "перенаправлена" на інший об'єкт. Посилання сама по собі нічого не представляє, у неї немає імені, вона сама – це інше ім'я для об'єкта. Взяття адреси посилання дає адреса об'єкту, на який вона посилається. Пам'ятайте: посилання – це об'єкт, на який вона посилається.


З цієї точки зору, посилання схожа на const покажчик [18.5], такий як int* const p (На відміну від покажчика на const [18.4], такого як const int* p ). Незважаючи на большую схожість, не плутайте посилання з вказівниками – це не одне і те ж.


[8.5] В яких випадках мені варто використовувати посилання, і в яких – покажчики?


Використовуйте посилання, коли можете, а покажчики – коли це необхідно.


Посилання зазвичай краще покажчиків, коли вам непотрібно їх "перенаправляти" [8.4]. Це зазвичай означає, що посилання особливо корисні у відкритій (public) частини класу. Посилання зазвичай з'являються на поверхні об'єкта, а покажчики заховані усередині.


Винятком є той випадок, коли параметр або повертається з функції об'єкт вимагає виділення "охоронного" значення для особливих випадків. Це зазвичай реалізується шляхом взяття / повернення покажчика, і позначенням особливого випадку за допомогою передачі нульового покажчика (NULL). Посилання ж не може посилатися на разименованний нульовий вказівник.


Примітка: програмісти з досвідом роботи на С часто недолюблюють посилання, через те що передача параметра за посиланням явно ніяк не позначається в зухвалій коді. Однак з набуттям деякого досвіду роботи на С + +, вони усвідомлюють, що це одна з форм приховування інформації, яка є швидше перевагою, ніж недоліком. Тобто, програмісту слід писати код в термінах задачі, а не комп'ютера (programmers should write code in the language of the problem rather than the language of the machine).


РОЗДІЛ [9]: Вбудовані ( inline ) Функції


[9.1] Що таке вбудована функція?


Вбудована функція – це функція, код якої прямо вставляється в тому місці, де вона викликана. Як і макроси, визначені через #define , Вбудовані функції покращують продуктивність за рахунок вартості виклику і (особливо!) за рахунок можливості додаткової оптимізації ("процедурна інтеграція").


[9.2] Як вбудовані функції можуть впливати на співвідношення безпеки та швидкості?


У звичайному С ви можете отримати "інкапсульовані структури", розміщуючи в них покажчик на void , І примушуючи його вказувати на справжні дані, тип яких невідомий користувачам структури. Таким чином, користувачі не знають, як інтерпретувати ці дані, а функції доступу перетворять покажчик на void до потрібного прихованого типу. Так досягається певний рівень інкапсуляції.


На жаль, цей метод йде врозріз з безпекою типів, а також вимагає виклику функції для доступу до будь-яких полів структури (якщо ви дозволили б прямий доступ, то його міг би отримати будь-хто, оскільки буде відомо, як інтерпретувати дані, на які вказує void* . Така поведінка з боку користувача призведе до складнощів при подальшій зміні структури підлягають даних).


Вартість виклику функції невелика, але дає деяку надбавку. Класи С + + дозволяють вбудовування функцій, що дає вам безпеку інкапсуляції разом зі швидкістю прямого доступу. Більш того, типи параметри вбудовуваних функцій перевіряються компілятором, що є перевагою в порівнянні з (?) сішнимі #define макросами.


[9.3] Навіщо мені використовувати вбудовані функції? Чому не використовувати просто #define макроси?


Оскільки #define макроси небезпечні [9.3], небезпечні [34.1], небезпечні [34.2], небезпечні [34.3].


На відміну від #define макросів, які вбудовані ( inline ) Функції несхильні відомим помилок подвійного обчислення, оскільки кожен аргумент вбудованої функції обчислюється тільки один раз. Іншими словами, виклик вбудованої функції – це те ж саме що і виклик звичайної функції, тільки швидше:

 / / Макрос, який повертає модуль (абсолютне значення) i
#define unsafe(i) ( (i) >= 0 ? (i) : -(i) )

/ / Вбудована функція, що повертає абсолютне значення i
inline
int safe(int i)
{
return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
int ans;
ans = unsafe (x + +); / / Помилка! x инкрементируется двічі
ans = unsafe (f ()); / / Небезпечно! f () викликається двічі
ans = safe (x + +); / / Вірно! x инкрементируется один раз
ans = safe (f ()); / / Вірно! f () викликається один раз
}


Також, на відміну від макросів, типи аргументів вбудованих функцій перевіряються, і виконуються всі необхідні перетворення.


Макроси шкідливі для здоров'я, не використовуйте їх, якщо це не необхідно.


Читати частина 2

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*