Passando riferimenti ai puntatori in C ++


130

Per quanto ne so, non c'è motivo per cui non dovrei essere autorizzato a passare un riferimento a un puntatore in C ++. Tuttavia, i miei tentativi di farlo stanno fallendo e non ho idea del perché.

Questo è quello che sto facendo:

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

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

E sto ricevendo questo errore:

impossibile convertire il parametro 1 da 'std :: string *' a 'std :: string * &'

Risposte:


126

La funzione prevede un riferimento a un puntatore di stringa effettivo nell'ambito di chiamata, non a un puntatore di stringa anonimo. Così:

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

dovrebbe compilare bene.

Tuttavia, questo è utile solo se si intende modificare il puntatore che si passa alla funzione. Se si intende modificare la stringa stessa, è necessario utilizzare un riferimento alla stringa come suggerito da Sake. Con questo in mente dovrebbe essere più ovvio il motivo per cui il compilatore si lamenta del tuo codice originale. Nel tuo codice il puntatore viene creato "al volo", modificando quel puntatore non avrebbe conseguenze e non è quello che si intende. L'idea di un riferimento (rispetto a un puntatore) è che un riferimento punta sempre a un oggetto reale.


86

Il problema è che stai cercando di associare un riferimento temporaneo al riferimento, che C ++ non consente a meno che non sia il riferimento const.

Quindi puoi eseguire una delle seguenti operazioni:

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

È particolarmente istruttivo scrivere il tipo come fa il signor Burr nell'esempio n. 2, string* const& valanziché un equivalente meno leggibile come ad es const string* &val. Ai miei occhi questo è chiaramente un riferimento costante , a un puntatore, a una stringa. Personalmente preferisco scrivere T const&invece che const T&nelle dichiarazioni per ottenere questo preciso chiarimento.
fish2000,

1
Nit pick (non una critica): per me la frase bind a temporary to the referenceè più chiara in questa risposta rispetto a quella di @Chris ' reference to an actual string pointer in the calling scope, not an anonymous string pointer. Indipendentemente da ciò, entrambi sono probabilmente corretti dal mio punto di vista.
kevinarpe,

1
@ fish2000 Non solo, const string*&e in string* const&realtà sono tipi diversi. Il primo è un non- constriferimento a a const string*, mentre il secondo è un constriferimento a a string*.
Justin Time - Ripristina Monica il

@ fish2000 attenzione a "preferire" T const&a const T&- come fa notare @Justin Time, sono tipi diversi. Può avere conseguenze a volte, ma in altre situazioni sarà assolutamente fondamentale per preferire uno o l'altro, molto simile preferendo inta double.
omatai,

3
@ fish2000 - uno dice "ecco un riferimento che puoi cambiare in qualcosa che non puoi cambiare" mentre l'altro dice "ecco un riferimento che non puoi cambiare in qualcosa che puoi cambiare". In questo esempio dato, semplicemente non puoi scambiare i due.
omatai,

10

Modificalo in:

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

MODIFICARE:

Viene chiamato ref-to-pointere non è possibile passare un indirizzo temporaneo come riferimento alla funzione. (a meno che non lo sia const reference).

Tuttavia, ho mostrato std::string* pS = &s;(puntatore a una variabile locale), il suo uso tipico sarebbe: quando vuoi che il chiamante cambi il puntatore stesso, non l'oggetto a cui punta. Ad esempio, una funzione che alloca memoria e assegna l'indirizzo del blocco di memoria allocato al suo argomento deve prendere un riferimento a un puntatore o un puntatore a puntatore:

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

}

5

&s produce un puntatore temporaneo alla stringa e non è possibile fare riferimento all'oggetto temporaneo.


4
Questo non è del tutto vero - puoi fare un riferimento a const temporaneo
1800 INFORMAZIONI

3

Provare:

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

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

o

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

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

Per quello che sto facendo, ho bisogno dell'indirizzo del puntatore e del puntatore stesso. Non voglio passare il puntatore per valore.
Alex,

Non capisci ancora cosa cerchi di realizzare. Non puoi avere "indirizzo del puntatore" di "stringa s", semplicemente perché "stringa s" non è un puntatore.
Sake,

@Alex: Suppongo che sia necessario rilevare se la stringa è esattamente la stessa di un'altra che si sta memorizzando e non solo se il loro contenuto è lo stesso. In tal caso, tenere presente che è possibile utilizzare l'indirizzo dell'operatore per un riferimento e verrà visualizzato l'indirizzo dell'oggetto di riferimento: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - dribeas,

3

EDIT: ne ho sperimentato alcuni e ho scoperto che le cose sono un po 'più sottili di quanto pensassi. Ecco cosa penso ora sia una risposta accurata.

&snon è un lvalue, quindi non è possibile creare un riferimento ad esso a meno che non si faccia riferimento al tipo di riferimento const. Quindi, ad esempio, non puoi farlo

string * &r = &s;

ma puoi farcela

string * const &r = &s;

Se inserisci una dichiarazione simile nell'intestazione della funzione, funzionerà.

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

C'è un altro problema, vale a dire i provvisori. La regola è che puoi ottenere un riferimento a un temporaneo solo se lo è const. Quindi in questo caso si potrebbe sostenere che & s è temporaneo e quindi deve essere dichiaratoconst nel prototipo della funzione. Da un punto di vista pratico non fa differenza in questo caso. (È un valore o un temporaneo. Ad ogni modo, si applica la stessa regola.) Tuttavia, a rigor di termini, penso che non sia un valore temporaneo ma un valore. Mi chiedo se c'è un modo per distinguere tra i due. (Forse è semplicemente definito che tutti i provvisori sono valori e tutti i non valori sono provvisori. Non sono un esperto dello standard.)

Detto questo, il tuo problema è probabilmente a un livello superiore. Perché vuoi un riferimento all'indirizzo di s? Se si desidera un riferimento a un puntatore a s, è necessario definire un puntatore come in

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

Se vuoi un riferimento so un puntatore a s, fai la cosa semplice.


1

Ho appena fatto uso di un riferimento a un puntatore per rendere sicuri tutti i puntatori in un albero binario cancellato tranne il root. Per rendere sicuro il puntatore non ci resta che impostarlo su 0. Non ho potuto rendere la funzione che elimina l'albero (mantenendo solo la radice) per accettare un riferimento a un puntatore poiché sto usando il root (questo puntatore) come primo input per attraversare sinistra e destra.

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

In conclusione, un riferimento a un puntatore non è valido quando il parametro formale è (questo) puntatore.


1

Benvenuti in C ++ 11 e riferimenti 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);
    // ...
}

Ora hai accesso al valore del puntatore (indicato da val), che è l'indirizzo della stringa.

È possibile modificare il puntatore e a nessuno importa. Questo è un aspetto di ciò che un valore è in primo luogo.

Attenzione: il valore del puntatore è valido solo fino alla myfunc()restituzione. Alla fine, è un temporaneo.


-1

So che è possibile passare riferimenti a puntatori, l'ho fatto la scorsa settimana, ma non ricordo quale fosse la sintassi, dato che il tuo codice sembra corretto al mio cervello in questo momento. Tuttavia un'altra opzione è quella di utilizzare i puntatori di puntatori:

Myfunc(String** s)

-8

myfunc ("string * & val") questo di per sé non ha alcun senso. "string * & val" implica "string val", * e & si annullano a vicenda. Finalmente non si può passare la variabile stringa a una funzione ("stringa val"). Solo i tipi di dati di base possono essere passati a una funzione, poiché altri tipi di dati devono passare come puntatore o riferimento. Puoi avere sia string & val che string * val in una funzione.


4
Sbagliato a tutti i livelli. Aggiorna la sintassi della dichiarazione var.
Ari,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.