Коли використовувати посилання проти покажчиків


381

Я розумію синтаксис та загальну семантику покажчиків порівняно з посиланнями, але як я повинен вирішити, коли більш-менш доцільно використовувати посилання чи покажчики в API?

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

Наприклад у наступному коді:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

З вказівником завжди (більше) очевидно, що відбувається, тому для API та подібних, де чіткість викликає велике занепокоєння, вказівники не є більш доцільними, ніж посилання? Чи означає це, що посилання повинні використовуватися лише тоді, коли це необхідно (наприклад operator++)? Чи є якісь проблеми щодо ефективності роботи з тим чи іншим?

РЕДАКТИРУВАННЯ (ЗАРАЗ):

Окрім дозволу NULL значень та роботи з необробленими масивами, схоже, вибір зводиться до особистих переваг. Я прийняв відповідь нижче, що посилається на керівництво по стилю C ++ Google , оскільки вони представляють думку про те, що "Посилання можуть бути заплутаними, оскільки вони мають синтаксис значення, але семантику вказівника".

У зв'язку з додатковою роботою, необхідною для очищення аргументів вказівника, які не повинні бути NULL (наприклад, add_one(0)буде викликати версію вказівника та перерву під час виконання), з точки зору ремонтопридатності має сенс використовувати посилання там, де ОБОВ'ЯЗКОВО повинен бути присутнім, хоча це соромно втратити синтаксичну ясність.


4
Схоже, ви вже прийняли рішення, яким саме користуватися, коли. Особисто я вважаю за краще передавати об'єкт, над яким я дію, незалежно від того, змінюю чи ні. Якщо функція бере вказівник, це говорить мені, що вона діє на покажчики, тобто використовує їх як ітератори в масиві.
Бенджамін Ліндлі

1
@Schnommus: Досить справедливо, я в основному використовую TextMate. Але я вважаю, що бажано, щоб сенс був очевидний з першого погляду.
connec

4
А як щодо add_one(a);незрозуміло, що aце буде змінено? У коді прямо написано: додайте його .
GManNickG

32
@connec: Посібник зі стилів Google C ++ не вважається хорошим посібником зі стилю C ++. Це посібник зі стилів для роботи зі старою базою коду C ++ від Google (тобто добре для їхніх матеріалів). Прийняття відповіді на основі цього нікому не допомагає. Щойно прочитавши ваші коментарі та пояснення, ви дійшли до цього питання з уже заданою думкою і просто шукаєте інших людей, щоб підтвердити вашу думку. В результаті ви ставите питання і відповідаєте на те, що хочете / очікуєте почути.
Мартін Йорк

1
Це просто фіксується, називаючи метод addOneTo(...). Якщо це не те, що ви хочете зробити, просто подивіться на декларацію.
Стефан

Відповіді:


296

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

Уникайте покажчиків, поки не зможете.

Причина полягає в тому, що вказівники ускладнюють дотримання / читання, менш безпечні та набагато небезпечніші маніпуляції, ніж будь-які інші конструкції.

Тож правилом є використання покажчиків, лише якщо іншого вибору немає.

Наприклад, повернення вказівника на об’єкт - допустимий варіант, коли функція може повернути nullptr в деяких випадках і передбачається, що це буде. Однак, кращим варіантом буде використання чогось подібного boost::optional.

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

У вашому прикладі немає сенсу використовувати вказівник як аргумент, оскільки:

  1. якщо ви подаєте nullptrв якості аргументу, ви збираєтесь у не визначеній поведінці;
  2. версія посилального атрибута не дозволяє (без легких поміток хитрощів) проблему з 1.
  3. версія атрибута посилання простіша для розуміння для користувача: ви повинні надати дійсний об’єкт, а не те, що може бути недійсним.

Якщо поведінка функції повинна працювати з певним об'єктом або без нього, то використання вказівника як атрибута говорить про те, що ви можете передавати nullptrяк аргумент, і це добре для функції. Це свого роду договір між користувачем та реалізацією.


49
Я не впевнений, що вказівники щось важче читають? Це досить проста концепція і дає зрозуміти, коли щось, можливо, буде змінено. Якщо я щось скажу, що важче читати, коли немає ознак того, що відбувається, чому б add_one(a)не повернути результат, а не встановити його за посиланням?
connec

46
@connec: Якщо add_one(a)це заплутано, то це тому, що воно неправильно названо. add_one(&a)була б така ж плутанина, тільки зараз ви можете збільшувати вказівник, а не об’єкт. add_one_inplace(a)уникне всякої плутанини.
Нікол Болас

20
Один момент, посилання можуть стосуватися пам'яті, яка може пройти так само легко, як і покажчики. Тож вони не обов'язково безпечніші за покажчики. Збереження та передача посилань може бути настільки ж небезпечним, як і покажчики.
Дуг Т.

6
@Klaim Я мав на увазі сирі покажчики. Я мав на увазі, що C ++ має вказівники, NULLі nullptr, і це їх є не просто так. І це не добре продуманий або навіть реалістичний рада, щоб дати "ніколи не використовувати покажчики" та / або "ніколи не використовувати NULL, завжди використовувати boost::optional". Це просто божевільно. Не зрозумійте мене неправильно, необроблені покажчики потрібні рідше в С ++, ніж у С, але все ж вони корисні, вони не такі "небезпечні", як люблять заявляти деякі С ++ (це теж перебільшення), і знову: коли простіше просто використовувати вказівник і return nullptr;вказати пропущене значення ... Навіщо імпортувати цілий Boost?

5
@Klaim "використовувати NULL - це погана практика" - тепер це просто смішно. І ifце застаріле, і його слід використовувати while() { break; }замість цього, правда? Крім того, не хвилюйтесь, я бачив і працював з великими базами коду, і так, якщо ви недбалі , то власність - це проблема. Якщо ви не дотримуєтесь умов, використовуйте їх послідовно і коментуйте та документуйте свій код. Але зрештою, я повинен просто використовувати C, бо я занадто тупий за C ++, правда?

62

Вистави точно такі ж, оскільки посилання реалізуються внутрішньо як покажчики. Таким чином, вам не потрібно про це турбуватися.

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

Деякі умови кодування (як-от Google ) передбачають, що завжди слід використовувати покажчики чи посилання const, оскільки посилання мають дещо незрозумілий синтаксис: вони мають референтну поведінку, але значення синтаксису.


10
Щоб додати трохи цього, керівництво Google щодо стилю говорить, що вхідні параметри функцій повинні бути посиланнями const, а вихідні - покажчиками. Мені це подобається, тому що це дає зрозуміти, коли ви читаєте підпис функції, що таке вхід, а що вихід.
День

44
@Dan: Посібник зі стилю Google - це старий код Google, і його не слід використовувати для сучасного кодування. Насправді це досить поганий стиль кодування нового проекту.
GManNickG

13
@connec: Дозвольте сказати так: null - цілком дійсне значення вказівника. Де б не було вказівника, я можу дати йому значення null. Ergo вашого другого варіанту add_oneбуде зламано : add_one(0); // passing a perfectly valid pointer value, Kaboom. Потрібно перевірити, чи недійсне воно. Деякі люди будуть ретортувати: "ну я просто задокументую, що моя функція не працює з нулем". Це добре, але тоді ви переможете мету питання: якщо ви збираєтеся заглянути в документацію, щоб побачити, чи нуль добре, ви також побачите декларацію функції .
GManNickG

8
Якби це було посиланням, ви б бачили, що це так. Такий реторт пропускає пункт, хоча: Посилання застосовують на мовному рівні, що він посилається на існуючий об'єкт, і не може бути нульовим, хоча покажчики не мають такого обмеження. Я думаю, що зрозуміло, що виконання мовного рівня є більш потужним та менш схильним до помилок, ніж забезпечення виконання документації. Деякі спробують повернутись до цього, сказавши: "Дивіться, нульова посилання:. int& i = *((int*)0);Це недійсна реторта. Проблема в попередньому коді полягає в використанні вказівника, а не з посиланням . Посилання ніколи не бувають нульовими, період.
GManNickG

12
Здрастуйте, у коментарях я відчував брак юристів з мови, тому дозвольте мені виправити: посилання, як правило, реалізуються покажчиками, але стандарт говорить, що такого немає. Реалізація, що використовує якийсь інший механізм, буде 100% скаргою.
Томас Боніні

34

Від C ++ FAQ Lite -

Користуйтеся посиланнями, коли можете, і вказівниками, коли потрібно.

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

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

Примітка: Програмістам старого рядка C іноді не подобаються посилання, оскільки вони надають опорну семантику, яка не є явною в коді абонента. Однак після досвіду C ++ можна швидко зрозуміти, що це форма приховування інформації, яка є активом, а не зобов'язанням. Наприклад, програмісти повинні писати код мовою проблеми, а не мовою машини.


1
Я думаю, ви можете стверджувати, що якщо ви використовуєте API, ви повинні ознайомитись з тим, що він робить, і знати, чи змінений параметр змінено чи ні ... щось врахувати, але я вважаю, що я згоден з програмістами C ( хоча у мене мало досвіду роботи з C). Я б додав, хоча цей чіткіший синтаксис приносить користь програмістам, а також машинам.
connec

1
@connec: Переконайтеся, що програміст C правильно відповідає своїй мові. Але не робіть помилки трактуючи C ++ як C. Це зовсім інша мова. Якщо ви ставитесь до C ++ як до C, то в кінцевому підсумку ви пишете те, що вважається рівнозначним C with class(що не є C ++).
Мартін Йорк

15

Моє правило:

  • Використовуйте покажчики для вихідних або вхідних / вихідних параметрів. Тож видно, що значення буде змінено. (Ви повинні використовувати &)
  • Використовуйте покажчики, якщо параметр NULL є прийнятним значенням. (Переконайтесь, що це constякщо вхідний параметр)
  • Використовуйте посилання для вхідного параметра, якщо він не може бути NULL і не є примітивним типом ( const T&).
  • Використовуйте вказівники або смарт-покажчики, повертаючи новостворений об’єкт.
  • Використовуйте покажчики або смарт-покажчики як члени структури або класу замість посилань.
  • Використовуйте посилання для отримання дозволу (наприклад, int &current = someArray[i])

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


14

Відмова: окрім того, що посилання не можуть бути НУЛЬНИми та не відновлюватися (тобто те не може змінити об'єкт, яким вони є псевдонім), воно дійсно зводиться до питання смаку, тому я не збираюся говорити "це краще".

Однак, я не згоден з вашим останнім твердженням у публікації, оскільки я не думаю, що код втрачає ясність із посиланнями. У вашому прикладі

add_one(&a);

може бути зрозумілішим ніж

add_one(a);

оскільки ви знаєте, що, швидше за все, значення a зміниться. З іншого боку, хоча, підпис функції

void add_one(int* const n);

дещо не зрозуміло: чи буде n одиничним цілим чи масивом? Іноді у вас є лише доступ до (погано документованих) заголовків і подібних підписів

foo(int* const a, int b);

непросто інтерпретувати з першого погляду.

Імхо, посилання настільки ж хороші, як і покажчики, коли не потрібно (повторне) виділення чи повторне перев’язування (у сенсі, поясненому раніше). Більше того, якщо розробник використовує лише покажчики для масивів, підписи функцій дещо менш неоднозначні. Не кажучи вже про те, що синтаксис операторів набагато легше читається з посиланнями.


Дякуємо за чітку демонстрацію того, де обидва рішення отримують і втрачають ясність. Я спочатку був у таборі покажчиків, але це має багато сенсу.
Зак Бівон-Коллін

12

Як і інші вже відповіли: посилання завжди використовувати, якщо на змінному NULL/ nullptrне є дійсно дійсним станом.

Точка зору Джона Кармака на цю тему подібна:

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

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Редагувати 2012-03-13

Користувач Брет Кунс правильно зауважує:

Стандарт C ++ 11 завершено. Я думаю, що настав час у цій темі згадати, що більшість кодів має чудово поєднуватись із поєднанням посилань, shared_ptr та unique_ptr.

Це правда, але питання все ще залишається навіть при заміні необроблених покажчиків на розумні покажчики.

Наприклад, обидва std::unique_ptrі std::shared_ptrможуть бути сконструйовані як "порожні" вказівники через конструктор за замовчуванням:

... це означає, що використання їх без перевірки, що вони не порожні, ризикує аварією, саме про це і йдеться в дискусії Дж. Кармака.

І тоді у нас є кумедна проблема "як ми передаємо розумний покажчик як параметр функції?"

Jon «сек відповідь на питання C ++ - надіслати адресу підвищення :: shared_ptr , і наступні коментарі показують , що навіть тоді, передаючи смарт - покажчик на копію або за посиланням не настільки чіткого зрізу , як хотілося б (я схвалюю себе в" посилання "за замовчуванням, але я можу помилитися).


1
Стандарт C ++ 11 завершено. Я думаю, що настав час у цій темі згадати, що більшість кодів має чудово поєднуватись із комбінацією посилань shared_ptr, та unique_ptr. Семантика власності та умови / параметри входу / виходу доглядаються за допомогою комбінації цих трьох частин та стійкості. У C ++ майже немає потреби в необроблених покажчиках, за винятком випадків, коли ми маємо справу зі застарілим кодом та дуже оптимізованими алгоритмами. Ті області, де вони використовуються, повинні бути максимально капсульованими та перетворювати будь-які необроблені покажчики в семантично відповідний "сучасний" еквівалент.
Брет Кунз

1
Багато часу розумні покажчики не повинні проходити навколо, а їх слід перевірити на наявність нуля, а потім об'єкт, що міститься, переданий за посиланням. Єдиний раз, коли ви насправді повинні пройти розумний покажчик - це коли ви переносите (унікальний_ptr) або ділите (shared_ptr) право власності з іншим об'єктом.
Люк Варта

@povman: Я повністю згоден: Якщо право власності не є частиною інтерфейсу (і якщо воно не буде змінено, воно не повинно бути), ми не повинні передавати інтелектуальний вказівник як параметр (або повернене значення). Справа стає дещо складнішою, коли право власності є частиною інтерфейсу. Наприклад, дискусія Саттера / Майєрса про те, як передати унікальний_птр як параметр: копією (Sutter) або посиланням r-значення (Meyers)? Антипатерн покладається на перехід вказівника до глобального shared_ptr, ризик того, що цей покажчик буде визнаний недійсним (рішення копіюватиме інтелектуальний покажчик на стек)
paercebal

7

Це не питання смаку. Ось кілька остаточних правил.

Якщо ви хочете звернутися до статично оголошеної змінної в межах, в якій вона була оголошена, тоді використовуйте посилання C ++, і це буде абсолютно безпечно. Це ж стосується і статично оголошеного інтелектуального вказівника. Приклад цього використання параметрів за допомогою посилання є прикладом цього використання.

Якщо ви хочете посилатися на будь-що з області, яка ширша, ніж сфера, в якій заявлено, тоді вам слід використовувати посилальний підрахунок розумного вказівника, щоб він був абсолютно безпечним.

Ви можете посилатися на елемент колекції з посиланням на синтаксичну зручність, але це не безпечно; елемент можна видалити в будь-який час.

Щоб безпечно зберігати посилання на елемент колекції, ви повинні використовувати посилальний підрахунок інтелектуального вказівника.


5

Будь-яка різниця в ефективності була б настільки мала, що не виправдовуватиметься менш зрозумілим підходом.

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

У будь-якому випадку я в основному з вами згоден. Мені не подобається, що функції, посилаючись на посилання, змінюють їх значення, коли не дуже очевидно, що саме ця функція робить. Я теж вважаю за краще використовувати в цьому випадку покажчики.

Коли вам потрібно повернути значення складного типу, я, як правило, віддаю перевагу посиланням. Наприклад:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

Тут ім'я функції дає зрозуміти, що ви отримуєте інформацію в масиві. Так що плутанини немає.

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


4

Скопійовано з вікі -

Наслідком цього є те, що в багатьох реалізаціях робота зі змінною з автоматичним або статичним терміном експлуатації через посилання, хоча синтаксично схожа на доступ до неї безпосередньо, може залучати приховані операції дереференції, які є дорогими. Посилання є синтаксично суперечливою особливістю C ++, оскільки вони затьмарюють рівень непрямості ідентифікатора; тобто на відміну від коду С, де покажчики зазвичай виділяються синтаксично, у великому блоці коду С ++ це може бути не відразу зрозуміло, якщо об'єкт, до якого звертається, визначений як локальна чи глобальна змінна чи чи є посилання (неявний покажчик) на деяке інше місце, особливо якщо код змішує посилання та покажчики. Цей аспект може зробити важко написаний код C ++ важчим для читання та налагодження (див. Псевдонім).

Я з цим на 100% погоджуюся, і саме тому я вважаю, що ви повинні використовувати посилання лише тоді, коли у вас є дуже вагомі причини для цього.


Я також погоджуюся з великою мірою, однак я приходжу до точки зору, що втрата вбудованого захисту від покажчиків NULL є занадто дорогим для чисто синтаксичних проблем, тим більше, що - хоча більш чітко - синтаксис покажчика є досить потворним все одно.
connec

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

3

Бали, які слід пам’ятати:

  1. Покажчики можуть бути NULL, посилання не можуть бути NULL.

  2. Посилання простіше у використанні, constїх можна використовувати для довідки, коли ми не хочемо змінювати значення і просто потрібна посилання у функції.

  3. Покажчик, який використовується в *той час, як посилання використовуються з a &.

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

  5. Ви можете мати покажчики на тип пустоти, int a=5; void *p = &a;але не може мати посилання на тип недійсності.

Вказівник проти посилання

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

Вирок, коли використовувати що

Вказівник : для масиву, списку посилань, реалізації дерев та арифметики вказівника.

Довідка : Параметри функцій та типи повернення.


2

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

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

Спочатку може здатися, що параметр у RefPhone(const SimCard & card)конструкторі передається посиланням, оскільки це перешкоджає передачі неправильних / нульових покажчиків на конструктор. Це якимось чином заохочує розподіл змінних у стеці та отримання переваг від RAII.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

Але тоді тимчаси приходять знищити ваш щасливий світ.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

Тож якщо ви сліпо дотримуєтесь посилань, ви скасовуєте можливість передачі недійсних покажчиків на можливість зберігання посилань на знищені об'єкти, що в основному має такий же ефект.

редагувати: Зауважте, що я дотримувався правила "Використовуйте посилання, де можна, вказівники, куди потрібно. Уникайте покажчиків, поки не зможете". з найбільш схваленої та прийнятої відповіді (інші відповіді також пропонують це). Хоча це має бути очевидним, приклад - не показувати, що посилання як такі погані. Однак їх можна неправомірно використовувати, як і вказівники, і вони можуть створювати власні загрози коду.


Існують такі відмінності між вказівниками та посиланнями.

  1. Що стосується передачі змінних, pass by reference виглядає як pass by value, але має семантику покажчика (діє як покажчик).
  2. Посилання не можна безпосередньо ініціалізувати до 0 (null).
  3. Посилання (посилання, не посилається на об'єкт) не можна змінювати (еквівалентно покажчику "* const").
  4. const посилання може приймати тимчасовий параметр.
  5. Місцеві посилання на const продовжують термін експлуатації тимчасових об'єктів

Враховуючи ці мої діючі правила, наступне.

  • Використовуйте посилання на параметри, які будуть використовуватися локально в межах функції.
  • Використовуйте покажчики, коли 0 (null) є прийнятним значенням параметра або вам потрібно зберегти параметр для подальшого використання. Якщо 0 (null) прийнятний, я додаю до параметра суфікс "_n", використовуйте захищений покажчик (наприклад, QPointer у Qt) або просто документуйте його. Ви також можете використовувати розумні покажчики. Ви повинні бути обережнішими зі спільними вказівниками, ніж із звичайними вказівниками (інакше ви можете закінчитись дизайном витоків пам'яті та безладдям).

3
Проблема у вашому прикладі полягає не в тому, що посилання є небезпечними, а в тому, що ви покладаєтесь на щось, що не належить до об'єкта, щоб зберегти ваших приватних членів. const SimCard & m_card;це просто погано написаний код.
пламенко

@plamenko Я побоююся, що ви не розумієте мету прикладу. Чи const SimCard & m_cardправильно це чи ні, залежить від контексту. Повідомлення в цьому дописі полягає не в тому, що посилання небезпечні (тхо, вони можуть бути, якщо можна постаратися). Повідомлення полягає в тому, що не слід сліпо дотримуватися мантри "використовувати посилання, коли це можливо". Приклад - результат агресивного використання доктрини "використання посилань, коли це можливо". Це повинно бути зрозуміло.
doc

Є дві речі, які мене турбують у вашій відповіді, тому що я думаю, що це може ввести в оману когось, хто намагається дізнатися більше про цю справу. 1. Пост однонаправлений і легко скластися враження, що посилання погані. Ви навели лише один приклад того, як не використовувати посилання. 2. Вам не було зрозуміло у вашому прикладі, що з цим не так. Так, тимчасовий отримає руйнінг, але це був не той рядок, який був неправильним, це реалізація класу.
пламенко

Ви практично ніколи не повинні мати таких членів const SimCard & m_card. Якщо ви хочете бути ефективними з тимчасовими розробками, додайте explicit RefPhone(const SimCard&& card)конструктор.
пламенко

@plamenko, якщо ви не можете прочитати з основним розумінням, то у вас є більша проблема, ніж просто ввести в оману мій пост. Я не знаю, як я міг бути більш зрозумілим. Подивіться на перше речення. Існує проблема з мантрою "використовувати посилання, коли це можливо"! Де в моєму дописі ви знайшли твердження, що посилання погані? Наприкінці мого допису ви написали, де використовувати посилання, то як ви дійшли до таких висновків? Це не пряма відповідь на питання?
док.

1

Нижче наведено деякі вказівки.

Функція використовує передані дані, не змінюючи їх:

  1. Якщо об’єкт даних малий, наприклад вбудований тип даних або мала структура, передайте його за значенням.

  2. Якщо об’єктом даних є масив, використовуйте вказівник, тому що це ваш єдиний вибір. Зробіть покажчик покажчиком на const.

  3. Якщо об'єкт даних має структуру великого розміру, використовуйте const покажчик або посилання const для підвищення ефективності програми. Ви економите час і простір, необхідні для копіювання структури або дизайну класу. Зробіть покажчик або посилання const.

  4. Якщо об'єкт даних є об'єктом класу, використовуйте const-посилання. Семантика дизайну класів часто вимагає використання посилання, що є основною причиною того, що C ++ додав цю функцію. Отже, стандартний спосіб передачі аргументів об’єкта класу є посиланням.

Функція змінює дані у викликовій функції:

1. Якщо об'єкт даних є вбудованим типом даних, використовуйте вказівник. Якщо ви помітите код на зразок fixit (& x), де x є int, цілком зрозуміло, що ця функція має намір змінити x.

2. Якщо об'єкт даних є масивом, використовуйте єдиний вибір: вказівник.

3. Якщо об'єкт даних є структурою, використовуйте посилання або вказівник.

4. Якщо об'єкт даних є об’єктом класу, використовуйте посилання.

Звичайно, це лише вказівки, і можуть бути причини, щоб робити різний вибір. Наприклад, cin використовує посилання на основні типи, щоб ви могли використовувати cin >> n замість cin >> & n.


0

Довідники чистіші та простіші у використанні, і вони краще допомагають приховувати інформацію. Однак, посилання не можуть бути перепризначені Якщо вам потрібно спочатку вказати на один об’єкт, а потім на інший, потрібно скористатися вказівником. Посилання не можуть бути нульовими, тому, якщо існує якийсь шанс, що відповідний об'єкт може бути недійсним, не слід використовувати посилання. Потрібно використовувати вказівник. Якщо ви хочете самостійно обробляти маніпуляції з об'єктом, тобто якщо ви хочете виділити простір пам’яті для об’єкта в Heap, а не на стеці, ви повинні використовувати покажчик

int *pInt = new int; // allocates *pInt on the Heap

0

Ви повинні мати такий приклад, як правильно написаний

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

Ось чому посилання краще, якщо це можливо ...


-1

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

Тепер на тему покажчиків проти посилань. ІМХО Я думаю, що чіткість стоїть понад усе. Як тільки я читаю неявну поведінку, пальці ніг починають скручуватися. Я погоджуюсь, що приємно неявна поведінка, що посилання не може бути NULL.

Перенаправлення покажчика NULL не є проблемою. це призведе до збоїв у вашій програмі та буде легко налагоджувати. Більшою проблемою є неініціалізовані покажчики, що містять недійсні значення. Це, швидше за все, призведе до пошкодження пам’яті, що спричинить невизначеність поведінки без чіткого походження.

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


-2

Для вказівників вам потрібно, щоб вони вказували на щось, тому вказівники коштують місця в пам'яті.

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

Що стосується довідки, то це не буде коштувати пам'яті. У вас є ціла змінна, і ви можете передавати її як опорну змінну. Це воно. Вам не потрібно створювати довідкову змінну спеціально для неї.


4
Ні. Функція, яка приймає покажчик, не вимагає присвоєння змінної вказівника: ви можете передавати тимчасову &address. Посилання, безумовно, коштуватиме пам’яті, якщо вона є членом об’єкта, і плюс, всі існуючі компілятори насправді реалізують посилання як адреси, тому ви нічого не економите з точки зору передачі параметрів або перенаправлення.
підкреслюй_26
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.