Передача посилань на покажчики в C ++


130

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

Це те, що я роблю:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

І я отримую цю помилку:

не може перетворити параметр 1 з 'std :: string *' в 'std :: string * &'

Відповіді:


126

Ваша функція очікує посилання на фактичний рядковий вказівник в області виклику, а не анонімний вказівник рядка. Таким чином:

string s;
string* _s = &s;
myfunc(_s);

повинні скласти просто чудово.

Однак це корисно лише в тому випадку, якщо ви маєте намір змінити покажчик, який ви передаєте функції. Якщо ви маєте намір змінити сам рядок, вам слід скористатись посиланням на рядок, як запропонував Sake. Зважаючи на це, має бути більш очевидним, чому компілятор скаржиться на ваш оригінальний код. У вашому коді вказівник створюється "на льоту", змінюючи цей покажчик не матиме наслідків, і це не те, що призначено. Ідея посилання (проти вказівника) полягає в тому, що посилання завжди вказує на фактичний об'єкт.


86

Проблема полягає в тому, що ви намагаєтеся прив’язати тимчасове до посилання, що C ++ не дозволяє, якщо посилання не є const.

Таким чином, ви можете зробити одне з наступного:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}


void myfunc2(string* const& val)
{
    // Do stuff to the string pointer
}

int main()
// sometime later 
{
    // ...
    string s;
    string* ps = &s;

    myfunc( ps);   // OK because ps is not a temporary
    myfunc2( &s);  // OK because the parameter is a const&
    // ...

    return 0;
}

Особливо повчально виписати тип, як це робить пан Берр у прикладі №2 - string* const& valзамість менш розбірливого еквівалента, наприклад, наприклад const string* &val. Для моїх очей це очевидно посилання на const , на вказівник, на рядок. Я особисто вважаю за краще писати T const&замість const T&декларацій, щоб отримати це точне роз'яснення.
fish2000

1
Nit pick (не критика): Для мене ця фраза bind a temporary to the referenceу цій відповіді чіткіша, ніж @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Незалежно, з моєї точки зору, ймовірно, обидва.
kevinarpe

1
@ fish2000 Мало того, const string*&але string* const&насправді бувають різних типів. Перший - не constпосилання на a const string*, а другий - constпосилання на a string*.
Час Джастіна - Відновіть Моніку

@ Fish2000 остерігатися «бажана» , T const&щоб const T&- в якості @Justin часу вказує, що різні типи. Він не може мати ніяких наслідків іноді, але в інших ситуаціях це буде абсолютно необхідно , щоб віддати перевагу один або інший, так само, як вважає за краще , intщоб double.
оматай

3
@ fish2000 - один говорить "ось посилання, що ви можете змінити те, що ви не можете змінити", а інший говорить "ось посилання, яке ви не можете змінити на те, що ви можете змінити". У наведеному прикладі ви просто не можете взаємозаміняти два.
оматай

10

Змініть його на:

  std::string s;
  std::string* pS = &s;
  myfunc(pS);

Редагувати:

Це викликається, ref-to-pointerі ви не можете передавати тимчасову адресу як посилання на функцію. (якщо це не так const reference).

Хоча я показав std::string* pS = &s;(вказівник на локальну змінну), типовим його використанням було б: коли ви хочете, щоб виклик міняв сам вказівник, а не об'єкт, на який він вказує. Наприклад, функція, яка виділяє пам'ять та присвоює адресу блоку пам'яті, який вона виділила своєму аргументу, повинна приймати посилання на вказівник або покажчик на вказівник:

void myfunc(string*& val)
{
//val is valid even after function call
   val = new std::string("Test");

}

5

&s створює тимчасовий вказівник на рядок, і ви не можете посилатися на тимчасовий об'єкт.


4
Це не зовсім вірно - ви можете посилатися на const тимчасовий
1800 ІНФОРМАЦІЯ

3

Спробуйте:

void myfunc(string& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(s);
    // ...
}

або

void myfunc(string* val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

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

Ще не розумію, що ти намагаєшся досягти. Ви не можете мати "адресу вказівника" "string s", просто тому, що "string s" не є вказівником.
Сак

@ Алекс: Я вважаю, що вам потрібно визначити, чи строка точно така ж, як і інша, яку ви зберігаєте, а не лише те, чи вміст їх однаковий. Якщо це так, зауважте, що ви можете використовувати адресу оператора для посилання, і він отримає вам адресу об'єкта, на який посилається: void f (std :: string const & s) {std :: string const * p = & s; }
Девід Родрігес - дрибес

3

EDIT: Я експериментував з деякими, і виявив, що дещо тонше, ніж я думав. Ось що я вважаю, що це точна відповідь.

&sне є значенням, тому ви не можете створити посилання на нього, якщо тип посилання не має посилання const. Так, наприклад, ви не можете цього зробити

string * &r = &s;

але ти можеш зробити

string * const &r = &s;

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

void myfunc(string * const &a) { ... }

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

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

string *p = &s;
myfunc(p);

Якщо ви хочете посилання на sабо вказівник s, виконайте прямо.


1

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

void BinTree::safe_tree(BinTree * &vertex ) {
    if ( vertex!=0 ) {  // base case
        safe_tree(vertex->left);    // left subtree.
            safe_tree(vertex->right);   //  right subtree.
          // delete vertex;  // using this delete causes an error, since they were deleted on the fly using inorder_LVR. If inorder_LVR does not perform delete to the nodes, then, use delete vertex;
        vertex=0;  // making a safe pointer
    }
} // end in

Знизу, посилання на покажчик недійсне, коли формальним параметром є (цей) покажчик.


1

Ласкаво просимо до C ++ 11 та посилання на оцінку:

#include <cassert>
#include <string>

using std::string;

void myfunc(string*&& val)
{
    assert(&val);
    assert(val);
    assert(val->c_str());
    // Do stuff to the string pointer
}

// sometime later 
int main () {
    // ...
    string s;
    myfunc(&s);
    // ...
}

Тепер у вас є доступ до значення вказівника (на який посилається val), яке є адресою рядка.

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

Будьте уважні: значення вказівника діє лише до myfunc()повернення. Нарешті, це тимчасово.


-1

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

Myfunc(String** s)

-8

myfunc ("string * & val"), це не має жодного сенсу. "string * & val" означає "string val", * і & скасовує один одного. Нарешті, не можна вставити змінну рядка у функцію ("string val"). У функцію можуть передаватися лише основні типи даних, для інших типів даних потрібно передавати як вказівник або посилання. Для функції можна мати або string & val, або string * val.


4
Неправильно на всіх рівнях. Будь ласка, оновіть синтаксис декларації var.
Арі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.