Przekazywanie odwołań do wskaźników w C ++


130

O ile wiem, nie ma powodu, dla którego nie powinienem mieć możliwości przekazywania referencji do wskaźnika w C ++. Jednak moje próby kończą się niepowodzeniem i nie mam pojęcia, dlaczego.

Oto co robię:

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

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

Otrzymuję ten błąd:

nie można przekonwertować parametru 1 z „std :: string *” na „std :: string * &”

Odpowiedzi:


126

Twoja funkcja oczekuje odwołania do rzeczywistego wskaźnika ciągu w zakresie wywołującym, a nie anonimowego wskaźnika ciągu. A zatem:

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

powinien się dobrze skompilować.

Jest to jednak przydatne tylko wtedy, gdy zamierzasz zmodyfikować wskaźnik przekazany do funkcji. Jeśli zamierzasz zmodyfikować sam łańcuch, powinieneś użyć odniesienia do łańcucha, zgodnie z sugestią Sake. Mając to na uwadze, powinno być bardziej oczywiste, dlaczego kompilator narzeka na Twój oryginalny kod. W twoim kodzie wskaźnik jest tworzony „w locie”, modyfikowanie tego wskaźnika nie miałoby konsekwencji i nie jest to zamierzone. Ideą odniesienia (a nie wskaźnika) jest to, że odniesienie zawsze wskazuje na rzeczywisty obiekt.


86

Problem polega na tym, że próbujesz powiązać tymczasowy z odwołaniem, na co C ++ nie zezwala, chyba że odwołanie tak const.

Możesz więc wykonać jedną z następujących czynności:

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;
}

Szczególnie pouczające jest zapisanie czcionki tak, jak zrobił to pan Burr w przykładzie nr 2 - string* const& valzamiast mniej czytelnego odpowiednika, jak np const string* &val. Dla moich oczu jest to wyraźnie odniesienie do stałej , do wskaźnika, do struny. Osobiście wolę pisać T const&zamiast const T&deklaracji, aby uzyskać to dokładne wyjaśnienie.
fish2000

1
Nit pick (nie krytyka): Dla mnie fraza bind a temporary to the referencejest jaśniejsza w tej odpowiedzi niż @Chris 'as reference to an actual string pointer in the calling scope, not an anonymous string pointer. Niezależnie od tego, z mojej perspektywy oba są prawdopodobnie poprawne.
kevinarpe

1
@ fish2000 Nie tylko to, const string*&a string* const&właściwie są to różne typy. Pierwsza to brak constodniesienia do a const string*, podczas gdy druga to constodniesienie do a string*.
Justin Time - Przywróć Monikę

@ fish2000 uważaj „prefering” T const&aby const T&- jak @Justin Czas zwraca uwagę, są to różne typy. To może mieć żadnych konsekwencji czasami, ale w innych sytuacjach będzie to absolutnie niezbędne do preferowania jednego lub drugiego, podobnie jak preferowanie intdo double.
omatai

3
@ fish2000 - jeden mówi: „tutaj jest odniesienie, które możesz zmienić na coś, czego nie możesz zmienić”, podczas gdy drugie mówi „tutaj jest odniesienie, którego nie możesz zmienić na coś, co możesz zmienić”. W podanym przykładzie po prostu nie możesz zamienić tych dwóch.
Omatai

10

Zmień to na:

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

EDYTOWAĆ:

Nazywa się to ref-to-pointeri nie można przekazać adresu tymczasowego jako odniesienia do funkcji. (chyba że takconst reference ).

Chociaż pokazałem std::string* pS = &s;(wskazałem na zmienną lokalną), jego typowym zastosowaniem byłoby: gdy chcesz, aby odbiorca zmienił sam wskaźnik, a nie obiekt, na który wskazuje. Na przykład funkcja przydzielająca pamięć i przypisująca adres bloku pamięci, którą zaalokowała do swojego argumentu, musi mieć odniesienie do wskaźnika lub wskaźnik do wskaźnika:

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

}

5

&s tworzy tymczasowy wskaźnik do łańcucha i nie możesz odwołać się do tymczasowego obiektu.


4
To nie jest do końca prawda - możesz odnieść się do const tymczasowe
1800 INFORMACJE

3

Próbować:

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

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

lub

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

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

Jednak do tego, co robię, potrzebuję adresu wskaźnika, a także samego wskaźnika. Nie chcę przekazywać wskaźnika według wartości.
Alex

Nadal nie rozumiesz, co próbujesz osiągnąć. Nie możesz mieć „adresu wskaźnika” w „łańcuchu s”, po prostu dlatego, że „ciąg s” nie jest wskaźnikiem.
Sake

@Alex: Rozumiem, że musisz wykryć, czy ciąg jest dokładnie taki sam jak inny, który przechowujesz, a nie tylko, czy ich zawartość jest taka sama. Jeśli tak jest, pamiętaj, że możesz użyć operatora address-of do odniesienia, a otrzymasz adres obiektu, do którego się odwołuje: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas

3

EDYCJA: trochę eksperymentowałem i odkryłem, że rzeczy są nieco subtelniejsze niż myślałem. Oto, co teraz uważam za dokładną odpowiedź.

&snie jest lwartością, więc nie można utworzyć do niej odwołania, chyba że jest to odniesienie do typu const. Na przykład nie możesz tego zrobić

string * &r = &s;

ale możesz to zrobić

string * const &r = &s;

Jeśli umieścisz podobną deklarację w nagłówku funkcji, zadziała.

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

Jest jeszcze jedna kwestia, a mianowicie prowizoryczne. Zasada jest taka, że ​​odwołanie do pliku tymczasowego można uzyskać tylko wtedy, gdy tak jest const. Więc w tym przypadku można by argumentować, że & s jest tymczasowe i dlatego musi być zadeklarowane constw prototypie funkcji. Z praktycznego punktu widzenia nie ma to znaczenia w tym przypadku. (Jest to albo wartość r, albo wartość tymczasowa. Tak czy inaczej, obowiązuje ta sama reguła.) Jednakże, mówiąc ściśle, myślę, że nie jest to wartość tymczasowa, ale wartość r. Zastanawiam się, czy istnieje sposób na rozróżnienie tych dwóch. (Być może jest po prostu zdefiniowane, że wszystkie wartości tymczasowe są wartościami r, a wszystkie wartości inne niż l są tymczasowe. Nie jestem ekspertem w zakresie standardów).

Biorąc to pod uwagę, twój problem jest prawdopodobnie na wyższym poziomie. Dlaczego chcesz podać adres s? Jeśli chcesz odwołać się do wskaźnika do s, musisz zdefiniować wskaźnik, tak jak w

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

Jeśli chcesz mieć odniesienie slub wskaźnik do s, zrób prostą rzecz.


1

Właśnie skorzystałem z odniesienia do wskaźnika, aby wszystkie wskaźniki w usuniętym drzewie binarnym z wyjątkiem katalogu głównego były bezpieczne. Aby wskaźnik był bezpieczny, musimy po prostu ustawić go na 0. Nie mogłem sprawić, aby funkcja, która usuwa drzewo (zachowując tylko korzeń), akceptowała odniesienie do wskaźnika, ponieważ używam korzenia (tego wskaźnika) jako pierwszego wejście do przejścia w lewo iw prawo.

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

Podsumowując, odwołanie do wskaźnika jest nieprawidłowe, gdy parametrem formalnym jest (ten) wskaźnik.


1

Witamy w C ++ 11 i referencjach rvalue:

#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);
    // ...
}

Teraz masz dostęp do wartości wskaźnika (do którego odwołuje się val), czyli adresu ciągu.

Możesz modyfikować wskaźnik i nikogo to nie obchodzi. To jest przede wszystkim jeden aspekt tego, czym jest wartość r.

Uważaj: wartość wskaźnika jest ważna tylko do momentu myfunc()powrotu. W końcu to tymczasowe.


-1

Wiem, że możliwe jest przekazywanie referencji wskaźników, zrobiłem to w zeszłym tygodniu, ale nie pamiętam, jaka była składnia, ponieważ Twój kod wygląda teraz poprawnie w moim mózgu. Jednak inną opcją jest użycie wskaźników wskaźników:

Myfunc(String** s)

-8

myfunc ("string * & val") to samo w sobie nie ma żadnego sensu. „string * & val” implikuje „string val”, * i & anulują się nawzajem. Wreszcie nie można przekazać zmiennej typu string do funkcji („string val”). Do funkcji można przekazywać tylko podstawowe typy danych, ponieważ inne typy danych muszą być przekazywane jako wskaźnik lub referencja. Do funkcji można przypisać string & val lub string * val.


4
Źle na wszystkich poziomach. Odśwież składnię deklaracji var.
Ari
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.