За яких обставин ви хотіли б використовувати такий код в C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
За яких обставин ви хотіли б використовувати такий код в C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Відповіді:
Вам потрібно буде передати вказівник за посиланням, якщо вам потрібно змінити вказівник, а не об’єкт, на який вказує вказівник.
Це схоже на те, чому використовуються подвійні покажчики; використання посилання на покажчик дещо безпечніше, ніж використання покажчиків.
delete
або перев'язуючи цей вказівник до якогось іншого місця в пам'яті? Або я помилявся?
[]
оператора. Одним із можливих (і насправді природних) підписів для цього буде const T &operator[](size_t index) const
. Але ти міг також мати T &operator[](size_t index)
. Ви могли б мати обидва одночасно. Останнє дозволить вам робити такі речі myArray[jj] = 42
. У той же час ви не надаєте вказівник на свої дані, тому абонент не може возитися з пам'яттю (наприклад, випадково видалити її).
50% програмістів на C ++ люблять встановлювати свої покажчики на нуль після видалення:
template<typename T>
void moronic_delete(T*& p)
{
delete p;
p = nullptr;
}
Без посилання ви змінювали б лише локальну копію вказівника, не впливаючи на абонента.
paranoid_delete
, але Щеня перейменував його на moronic_delete
. Ймовірно, він належить до інших 50%;) У будь-якому випадку, коротка відповідь полягає в тому, що вказівники налаштувань на нуль після delete
майже не бувають корисними (оскільки вони в будь-якому випадку повинні вийти за межі сфери дії) і часто заважає виявленню помилок "використання після безкоштовного".
delete
це взагалі дурість. Щеня, я кілька разів гуглив, я не розумію, чому видалення абсолютно марне (можливо, я теж жахливий гугл;)). Ви можете пояснити трохи більше або надати посилання?
Відповідь Девіда правильна, але якщо вона все ж трохи абстрактна, ось два приклади:
Можливо, ви захочете обнулити всі звільнені вказівники, щоб раніше виявити проблеми з пам'яттю. Ви б виконали C-стиль:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
У C ++, щоб зробити те ж саме, ви можете зробити:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
При роботі зі зв’язаними списками - часто просто представляються як вказівники на наступний вузол:
struct Node
{
value_t value;
Node* next;
};
У цьому випадку, коли ви вставляєте в порожній список, ви обов'язково повинні змінити вхідний вказівник, оскільки результат вже не є NULL
вказівником. Це випадок, коли ви модифікуєте зовнішній вказівник на функцію, тому в її підписі буде посилання на вказівник:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
У цьому питанні є приклад .
Мені доводилося використовувати такий код, щоб надавати функції для виділення пам'яті в переданий покажчик та повернення його розміру, оскільки моя компанія "об'єкт" мені за допомогою STL
int iSizeOfArray(int* &piArray) {
piArray = new int[iNumberOfElements];
...
return iNumberOfElements;
}
Це не приємно, але вказівник повинен передаватися за посиланням (або використовувати подвійний вказівник). Якщо ні, пам'ять виділяється локальній копії вказівника, якщо вона передається за значенням, що призводить до витоку пам'яті.
Одним із прикладів є написання функції синтаксичного аналізатора та передача йому вихідного вказівника для читання, якщо функція повинна просунути цей покажчик вперед за останнім символом, який був правильно розпізнаний парсером. Тоді використання посилання на покажчик дає зрозуміти, що функція перемістить оригінальний покажчик, щоб оновити своє положення.
Як правило, ви використовуєте посилання на покажчики, якщо хочете передати вказівник на функцію і дозволити їй перемістити оригінальний вказівник в інше положення, а не просто переміщати його копію, не впливаючи на оригінал.
Інша ситуація, коли це вам може знадобитися, це якщо у вас є колекція покажчиків stl і ви хочете змінити їх за допомогою алгоритму stl. Приклад for_each в c ++ 98.
struct Storage {
typedef std::list<Object*> ObjectList;
ObjectList objects;
void change() {
typedef void (*ChangeFunctionType)(Object*&);
std::for_each<ObjectList::iterator, ChangeFunctionType>
(objects.begin(), objects.end(), &Storage::changeObject);
}
static void changeObject(Object*& item) {
delete item;
item = 0;
if (someCondition) item = new Object();
}
};
В іншому випадку, якщо ви використовуєте підпис changeObject (Object * item), у вас є копія вказівника, а не оригінальний.