2016-07-23 4 views
0

Warum wird die T* operator->() wiederholt angewendet, auch wenn sie einmal geschrieben wurde? Aber ein weiterer T& operator*() wird einmal angewendet und sollte viele Male geschrieben werden.Warum wird der `T * operator ->()` wiederholt angewendet, auch wenn er einmal geschrieben wurde?

Wie bekannt gibt es Execute-Around Pointer Idiom in C++. More C++ Idioms/Execute-Around Pointer

Stellen Sie ein Smart-Pointer-Objekt bereit, das Aktionen vor und nach jedem Funktionsaufruf für ein Objekt transparent ausführt, da die ausgeführten Aktionen für alle Funktionen identisch sind. Und vor und nach jeder Behandlung zur Mitgliedsvariablen einer Klasse. Zum Beispiel können wir durch:

  • Sperren-Mutex
  • log Aktion
  • visualisieren Ändern von Daten

Ich habe einige in main()-this example:

#include <iostream> 
#include <vector> 

class VisualizableVector { 
    public: 
    class proxy { 
     public: 
     proxy (std::vector<int> *v) : vect (v) { 
      std::cout << "Before size is: " << vect->size() << std::endl; 
     } 
     std::vector<int> * operator ->() { return vect; } 
     std::vector<int> & operator *() { return *vect; } 
     ~proxy() { std::cout << "After size is: " << vect->size() << std::endl; } 
     private: 
     std::vector <int> * vect; 
    };   
    VisualizableVector (std::vector<int> *v) : vect(v) {}    
    ~VisualizableVector() { delete vect; } 
    proxy operator ->() { return proxy (vect); } 
    proxy operator *() { return proxy (vect); } 
    private: 
    std::vector <int> * vect; 
}; 

int main() 
{ 
    VisualizableVector vecc (new std::vector<int>); 

    vecc->push_back (10);   // 1. Note use of -> operator instead of . operator  
    vecc->push_back (20);   // 2. ok  
    (*vecc)->push_back (30);  // 3. ok  
    // (*vecc).push_back (40); // 4. error  
    (**vecc).push_back (50);  // 5. ok  
    // vecc->->push_back (60); // 6. error  
} 

Online-Compiler Ergebnis: http://ideone.com/cXGdxW

Warum müssen wir zweimal schreiben **, aber nur einmal ->?

Die Betreiber geben die gleiche Sache proxy:

proxy operator ->() { return proxy (vect); } 
    proxy operator *() { return proxy (vect); } 

Aber warum brauchen wir * wieder verwenden, aber wir sollten -> nicht wieder verwenden ?:

vecc->push_back (20);  // 2. ok  (vecc->) is proxy 
    (**vecc).push_back (50); // 5. ok  (*vecc) is proxy 

Warum vecc->->push_back (20); nicht?

Gibt es irgendetwas darüber in der Norm C++ (03/11/14)?

UPDATE:

In verschiedenen Fällen sollten wir 1,2 oder 3 operator-> s verwenden: http://ideone.com/89kfYF

#include <iostream> 
#include <vector>  
class VisualizableVector { 
    public: 
    class proxy { 
     public: 
     proxy (std::vector<int> *v) : vect (v) { 
      std::cout << "Before size is: " << vect->size() << std::endl; 
     } 
     std::vector<int> * operator ->() { return vect; } 
     std::vector<int> & operator *() { return *vect; } 
     ~proxy() { std::cout << "After size is: " << vect->size() << std::endl; } 
     private: 
     std::vector <int> * vect; 
    };   
    VisualizableVector (std::vector<int> *v) : vect(v) {}    
    ~VisualizableVector() { delete vect; } 
    proxy operator ->() { return proxy (vect); } 
    proxy operator *() { return proxy (vect); } 
    private: 
    std::vector <int> * vect; 
}; 

int main() 
{ 
    VisualizableVector vecc (new std::vector<int>); 

    vecc->push_back(30);   // ok  // one -> 
    //vecc.operator->().push_back(30);// error // one -> 

    //vecc->->push_back(30);   // error // two -> 
    vecc.operator->()->push_back(30); // ok  // two -> 

    auto proxy3 = vecc.operator->();  // 1st operator->() 
    auto pointer = proxy3.operator->(); // 2nd operator->() 
    pointer->push_back(30);    // 3rd operator->()  
    return 0; 
} 

Seite 327: Working Draft, Standard for Programming Language C++ 2014-11-19

13.5.6 Klasse Mitglied Zugang [over.ref] 1 operator-> soll eine nicht statische Memberfunktion sein, die keine Parameter annimmt. Es implementiert die Klassenmemberzugriffssyntax, die -> verwendet. postfix-expression -> templateopt id-ausdruck postfix-ausdruck -> pseudo-destruktorname Ein Ausdruck x-> m wird interpretiert als (x.operator ->()) -> m für eine Klasse Objekt x des Typs T wenn T :: operator ->() existiert und wenn der Operator als die beste Übereinstimmungsfunktion durch die Überladungsauflösung (13.3) ausgewählt ist.

I.e. x->m ist (x.operator->())->m.

+0

Die kurze Antwort lautet: Weil C++ so funktioniert und Sie Ihre Klassen so geschrieben haben. –

+0

Seitennotiz: Dies ist eine schlechte Frage, wegen der unnötigen Verwendung von "neu" (ohne Pairing zu "löschen") –

+0

IMHO, 'operator->' und 'operator *' Rückgabe genau der gleichen Art, ist schlecht an sich. –

Antwort

2

a->b ist definiert als (*a).b wenn und nur wenn a ist ein Zeiger.

Wenn a kein Zeiger ist, wird er als (a.operator->())->b definiert. Nun gibt typischerweise einen Zeiger zurück, also macht es dann einen (*(a.operator->())).b und fertig.

Wenn aber stattdessen ein Nicht-Zeiger zurückgegeben wird, ist diese Definition rekursiv.

Es gibt keine ähnliche rekursive Definition für unäre operator*.

Kurz gesagt, sagt der Standard. Warum? Weil die Autoren dachten, es wäre sowohl elegant als auch nützlich.

Als beiseite, es ist ein aktiver Vorschlag für operator., die wahrscheinlich in C++ sein wird als 2021 Diese (*a).b erlauben würde, die gleichen wie a->b zu verhalten.

+0

Danke! I.e. 'x-> m' ist das gleiche' (x.operator ->()) -> m' für Objekte (keine Zeiger) und immer rekursiv. Dies ist in der Norm Seite 327 definiert: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf – Alex

1

Hier sind diese beiden Fälle gebrochen etwas nach unten:

(*vecc)->push_back(30);  // 3. ok  
VisualizableVector::proxy proxy3 = vecc.operator*(); 
std::vector<int> *pointer = proxy3.operator->(); 
pointer->push_back(30); 

(**vecc).push_back(50);  // 5. ok 
VisualizableVector::proxy proxy5 = vecc.operator*(); 
std::vector<int> &reference = proxy5.operator*(); 
reference.push_back(50); 

Der Grund müssen Sie dereferenzieren mit * doppelt so groß ist, weil Proxy :: operator *() einen Zeiger auf den zugrunde liegenden Typ zurückgibt.

Wenn Sie einen Zeiger haben, können Sie seine Mitglieder direkt mit "->" aufrufen, oder Sie können ihn mit "*" dereferenzieren und dann "." Dies bleibt unabhängig davon, woher der Zeiger kam.

Da Sie den Zeiger von * erhalten haben und Sie * auf diesen Zeiger verwenden, haben Sie deshalb zwei * verwendet.

+0

Entschuldigung, ich überschrieb jemandes simultane Bearbeitung, die versucht, an der Formatierung zu arbeiten und meine Schreibweise konsistent zu machen. Kannst du dem * wieder entkommen oder mir sagen, wie es geht? –

+0

Danke. Ja, ich kann doppelte '**' vermeiden, wenn ich den Cast-Operator 'proxy :: operator std :: vector &() {return * vect; } 'und verwende implizite/explizite' static_cast'. Zum Beispiel implizite 'std :: vector & vec_ref = * vecc;' oder explizite 'static_cast > (* vecc) .push_back (40); // 4.a ok' http://ideone.com/IlvhPc – Alex

Verwandte Themen