Fragen zum Operator new () und Operator delete ()

  • Berücksichtigen Sie den folgenden Code und die folgenden Fragen:

     /*
    * GCC 4.4
    */
    #include <iostream>
    
    using namespace std;
    
    class A {
    public:
    
        void* operator new(size_t s) {
            cout << "A::operator new(size_t) called\n";
        }
    
        void operator delete(void* p) {
            cout << "A::operator delete(void*) called\n";
        }
    
        void* operator new(size_t s, A* p) {
            cout << "A::operator new(size_t, A*) called\n";
        }
    
        void operator delete(void* p, size_t s) {
            cout << "A::operator delete(void*, size_t) called\n";
        }
    
    };
    
    void* operator new(size_t s) {
        cout << "::operator new(size_t) called\n";
    }
    
    void operator delete(void* p) {
        cout << "::operator delete(void*) called\n";
    }
    
    void* operator new(size_t s, A* p) {
        cout << "::operator new(size_t, A*) called\n";
    }
    
    void operator delete(void* p, size_t s) {
        cout << "::operator delete(void*, size_t) called\n";
    }
    
    int main() {    
        A* p1 = new A(); // See question 1.
        delete p1; // See question 2.
        A* p2 = new (p1) A(); // See question 3.
        delete p2; // See question 4.
    }
     

    Die folgenden Fragen mag irgendwie überflüssig erscheinen. Was ich jedoch zu unterscheiden versuche, ist das, was durch die C ++ - Standardregeln von dem definiert wird, was durch die Implementierung definiert wird.

    1. operator new(size_t) wird in jedem Fall verwendet (aus A oder aus dem globalen Namespace, standardmäßig oder nicht). Dies ist in Ordnung. Versuchen Sie nun, nur void* A::operator new(size_t) {} zu entfernen: Warum gibt der Compiler Folgendes aus:

      Fehler: Keine passende Funktion für Aufruf an ' Ein :: Operator new (unsigned int) ' Hinweis: Kandidaten sind: static void * A :: operator new (size_t, A *)

      Kann der Compiler nicht up ::operator new(size_t) aus dem globalen Namespace auswählen?

    2. Warum is operator delete(void*) bevorzugt to operator delete(void*, size_t) (wenn beide Versionen im selben Namespace vorhanden sind, von dem ere operator delete (void*) entnommen wurde)?

    3. Warum wird der Code nicht kompiliert? wenn ich rem ove void* A::operator new(size_t, A*), obwohl es dieselbe Version des Operators gibt, der im globalen Namespace definiert ist?

      Fehler: keine Übereinstimmung Funktion für Aufruf an 'A :: operator new (unsigned int, A * & amp;)' Hinweis: Kandidaten sind: statisch void * A :: operator new (size_t)

      < / blockquote>
    4. Warum ist der Compiler noch vor fer operator delete (void*) , obwohl A :: operator new (size_t, A *) ist wurde verwendet, um p zu erhalten 2?

    22 November 2011
    Martin
3 answers
  • Ihre erste und dritte Frage funktioniert IMO folgendermaßen:

    1. Da Sie :: new nicht verwendet haben, wird die Der Compiler versucht, einen neuen Operator in der A-Klasse zu finden.
    2. Er findet einen, aber er kann die richtige Überladung nicht finden, daher schlägt er fehl.

    Wenn Sie explizit ::new angeben, sollte es keine Probleme geben. Der Compiler geht nur in den globalen Namespace, wenn er die spezialisierte Version des neuen Operators in der Klasse nicht finden kann.

    Im Standard: § 5.3.4,

    9. Wenn der neue Ausdruck mit einem unary :: -Operator beginnt, wird der Name der Zuweisungsfunktion im globalen Gültigkeitsbereich gesucht . Wenn der zugewiesene Typ ein Klassentyp T oder ein Array davon ist, wird der Name der Zuweisungsfunktion im Gültigkeitsbereich von T gesucht. Wenn diese Suche den Namen nicht findet, oder wenn der zugewiesene Typ kein <| ist > Klassentyp, der Name der Allokationsfunktion wird im globalen Gültigkeitsbereich gesucht.

    22 November 2011
    sehecw.prime
  • Stellen wir uns einige Szenarien vor. Zuallererst funktionieren die folgenden immer :

     A * p1 = ::new A;
    ::delete p1;
    
    A * p2 = ::new (addr) A;        // assume "void * addr" is valid
    p2->~A();
     

    Die globalen Ausdrücke verwenden immer das entsprechende Operatoren aus dem globalen Namespace, also geht es uns gut. Beachten Sie, dass kein Ausdruck zum Platzieren / Löschen vorhanden ist . Jedes Platzierungsobjekt muss explizit durch Aufruf des Destruktors zerstört werden.

    Nehmen wir an, wir schreiben:

     A * p3 = new A;
    delete p3;
     

    Diesmal wird die Zuweisungsfunktion operator new(size_t) zuerst im Geltungsbereich von A nachgeschlagen. Der Name ist vorhanden, aber wenn Sie die richtige Überladung entfernen, liegt ein Fehler vor. (Dies beantwortet Q1 und Q3.) Dasselbe gilt für operator delete(). Es gibt keinen besonderen Grund, warum die Ein-Parameter-Version ((void *)) der Zwei-Argument-Version ((void *, size_t)) vorgezogen wird. Sie sollten nur eine der beiden haben. (Beachten Sie auch, dass es keine globale -Version der Funktion mit zwei Argumenten gibt.)

    Lassen Sie uns schließlich neue Platzierungsausdrücke betrachten. Da kein Platzierungs-Löschausdruck vorhanden ist, ist Ihre letzte Codezeile ein Fehler (undefiniertes Verhalten): Sie dürfen delete nichts angeben, das nicht durch einen Standardausdruck - new - Ausdruck erhalten wurde . Wenn Sie eine Platzierungs-Neuzuordnungsfunktion definieren, sollten Sie auch die entsprechende Freigabefunktion definieren. Diese Funktion wird jedoch nur in einer bestimmten Situation automatisch aufgerufen: Wenn der Platzhalter-Ausdruck new (a, b, c) Foo; dazu führt, dass der Konstruktor eine Ausnahme auslöst, wird die entsprechende Freigabefunktion aufgerufen. Andernfalls rufen Sie die Platzierungsfreigabefunktion normalerweise nur manuell auf (da sie alle manuell erstellt wird (und oftmals überhaupt nicht), da sie selten tatsächlich Arbeit leistet).

    Die Ein typisches Szenario könnte etwa wie folgt aussehen:

     void * addr = ::operator new(sizeof(Foo)); // do real work
    
    Foo * p = new (addr, true, 'a') Foo;       // calls Foo::operator new(void*, bool, char);,
                                               // then calls the constructor Foo::Foo()
    
    // in case of exception, call Foo::operator delete(addr, true, 'a')
    
    p->~Foo();
    
    Foo::operator delete(addr, true, 'a');      // rarely seen in practice, often no purpose
    
    ::operator delete(addr);                    // do real work
     

    Um den Eröffnungscode zu schließen, beachten Sie, dass Standard verlangt, dass die globalen ::operator delete(void *, void *) nichts tun. Das heißt, gl

    22 November 2011
    Kerrek SB
  • Ein Objekt erinnert sich nicht daran, wie es erstellt wurde. Platzierungslöschung wird nur verwendet, wenn die entsprechende Platzierung neu ausgelöst wird, andernfalls wird der reguläre Löschoperator verwendet.

    22 November 2011
    Neil