Sovraccarico degli operatori di accesso dei membri ->,. *


129

Capisco la maggior parte overloading degli operatori, con l'eccezione degli operatori di accesso utente ->, .*, ->*etc.

In particolare, cosa viene passato a queste funzioni dell'operatore e cosa deve essere restituito?

In che modo l'operatore (ad es. operator->(...)) Sa a quale membro viene fatto riferimento? Può saperlo? Deve nemmeno saperlo?

Infine, ci sono delle considerazioni che devono essere prese in considerazione? Ad esempio, quando sovraccarichi qualcosa del genere operator[], generalmente avrai bisogno sia di una versione const che di una non-const. Gli operatori di accesso dei membri richiedono versioni const e non const?


1
Credo che il C ++ sopra - Faq tocchi tutti i Q richiesti nel precedente Q.
Alok Salva l'

conste non sono richiesteconst versioni di , ma fornire entrambi può essere utile. operator->
Fred Foo,


9
@Als: le FAQ non spiegano come sovraccaricare ->*e .*. In realtà, non li menziona nemmeno! Sento che sono rari essere in una FAQ, ma collegherei volentieri questa domanda dalle FAQ. Si prega di non chiudere questo come duplicato delle FAQ!
sabato

@sbi, non sono riuscito a trovare un collegamento a questa domanda dalle tue (fantastiche) FAQ e ho finito per porre una domanda duplicata. Potresti renderlo più ovvio? (si scusa se è già ovvio).
P

Risposte:


144

->

Questo è l'unico davvero complicato. Deve essere una funzione membro non statica e non accetta argomenti. Il valore restituito viene utilizzato per eseguire la ricerca dei membri.

Se il valore restituito è un altro oggetto di tipo classe, non un puntatore, anche la successiva ricerca dei membri viene gestita da una operator->funzione. Questo si chiama "comportamento drill-down". La lingua mette insieme le operator->chiamate fino a quando l'ultima non restituisce un puntatore.

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

Questo è solo difficile in quanto non c'è nulla di speciale. La versione non sovraccaricata richiede un oggetto puntatore al tipo di classe sul lato sinistro e un oggetto puntatore al tipo di membro sul lato destro. Ma quando lo sovraccarichi, puoi prendere qualsiasi argomento ti piaccia e restituire tutto quello che vuoi. Non deve nemmeno essere un membro non statico.

In altre parole, questo è solo un operatore binario normale come +, -e /. Vedi anche: Operatore libero -> * sovraccarichi malvagi?

.* e .

Questi non possono essere sovraccarichi. C'è già un significato incorporato quando il lato sinistro è di tipo classe. Forse avrebbe poco senso essere in grado di definirli per un puntatore sul lato sinistro, ma il comitato di progettazione linguistica ha deciso che sarebbe stato più confuso che utile.

Il sovraccarico ->, ->*, ., e .*può riempire solo nei casi in cui l'espressione sarebbe indefinito, non potrà mai cambiare il significato di un'espressione che sarebbe valida senza sovraccarico.


2
La tua ultima affermazione non è completamente vera. Ad esempio, è possibile sovraccaricare l' newoperatore, anche se è valido anche se non sovraccarico.
Matt,

6
@Matt bene, newè sempre sovraccarico, o le regole di sovraccarico non si applicano realmente ad esso (13.5 / 5: Le funzioni di allocazione e deallocazione, operatore nuovo, operatore nuovo [], operatore cancellazione ed operatore cancellazione [], sono descritte completamente in 3.7 .4. Gli attributi e le restrizioni presenti nel resto di questa sottoclausola non si applicano a loro se non esplicitamente indicato nel 3.7.4.) Ma sovraccarico unario &o binario &&, ||o ,, o l'aggiunta di sovraccarichi operator=o sovraccarico qualsiasi cosa per un senza ambito tipo di enumerazione, può cambiare il significato di un'espressione. Chiarito l'affermazione, grazie!
Potatoswatter,

41

Operatore -> è speciale.

"Ha ulteriori vincoli atipici: deve restituire un oggetto (o riferimento a un oggetto) che ha anche un operatore di dereference puntatore, oppure deve restituire un puntatore che può essere usato per selezionare a cosa punta la freccia dell'operatore di dereference puntatore. " Bruce Eckel: Thinking CPP Vol-one: operator->

La funzionalità extra è fornita per comodità, quindi non è necessario chiamare

a->->func();

Puoi semplicemente fare:

a->func();

Ciò rende l'operatore -> diverso dagli altri sovraccarichi dell'operatore.


3
Questa risposta merita più credito, puoi scaricare il libro di Eckel da quel link e le informazioni sono nel capitolo 12 del primo volume.
P

26

Non è possibile sovraccaricare l'accesso dei membri .(ovvero la seconda parte di ciò che ->fa). Tuttavia, è possibile sovraccaricare il unario dereferenziazione dell'operatore *(cioè la prima parte di quello che ->fa).

L' ->operatore C ++ è fondamentalmente l'unione di due passaggi e questo è chiaro se pensi che x->ysia equivalente a (*x).y. C ++ ti consente di personalizzare cosa fare della (*x)parte quando xè un'istanza della tua classe.

La semantica per il ->sovraccarico è alquanto strana perché C ++ consente di restituire un puntatore regolare (che verrà utilizzato per trovare l'oggetto appuntito) o di restituire un'istanza di un'altra classe se questa classe fornisce anche un ->operatore. Quando in questo secondo caso la ricerca per l'oggetto con riferimento continua da questa nuova istanza.


2
Grande spiegazione! Immagino che significhi lo stesso per ->*, in quanto equivale alla forma di (*x).*?
Bingo

10

L' ->operatore non sa a quale membro viene indicato, fornisce semplicemente un oggetto su cui eseguire l'accesso effettivo del membro.

Inoltre, non vedo alcun motivo per cui non è possibile fornire versioni const e non const.


7

Quando si sovraccarica l'operatore -> () (qui non viene passato alcun argomento), ciò che il compilatore fa effettivamente sta chiamando -> in modo ricorsivo fino a quando non restituisce un puntatore effettivo a un tipo. Quindi utilizza il membro / metodo corretti.

Ciò è utile, ad esempio, per creare una classe di puntatore intelligente che incapsula il puntatore effettivo. Viene chiamato l'operatore sovraccarico->, fa qualunque cosa faccia (es. Blocco per la sicurezza del thread), restituisce il puntatore interno e quindi il compilatore chiama -> per questo puntatore interno.

Per quanto riguarda la costanza, è stata data risposta nei commenti e in altre risposte (è possibile e si dovrebbe fornire entrambi).

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.