51 Stimmen

Wie verhindere ich, dass eine Klasse über den "new"-Operator zugewiesen wird? (Ich möchte sicherstellen, dass meine RAII-Klasse immer auf dem Stack zugewiesen wird).

Ich möchte sicherstellen, dass meine RAII-Klasse immer auf dem Stack zugewiesen wird.

Wie verhindere ich, dass eine Klasse über den "new"-Operator zugewiesen wird?

60voto

Kevin Punkte 24023

Alles, was Sie tun müssen, ist, den neuen Operator der Klasse als privat zu deklarieren:

class X
{
    private: 
      // Prevent heap allocation
      void * operator new   (size_t);
      void * operator new[] (size_t);
      void   operator delete   (void *);
      void   operator delete[] (void*);

    // ...
    // The rest of the implementation for X
    // ...
};  

Wenn man 'operator new' privat macht, wird verhindert, dass Code außerhalb der Klasse 'new' verwendet, um eine Instanz von X zu erzeugen.

Der Vollständigkeit halber sollten Sie "operator delete" und die Array-Versionen der beiden Operatoren ausblenden.

Seit C++11 können Sie die Funktionen auch explizit löschen:

class X
{
// public, protected, private ... does not matter
    static void *operator new     (size_t) = delete;
    static void *operator new[]   (size_t) = delete;
    static void  operator delete  (void*)  = delete;
    static void  operator delete[](void*)  = delete;
};

Verwandte Frage: Ist es möglich, die Stack-Allokation eines Objekts zu verhindern und nur zuzulassen, dass es mit "new" instanziiert wird?

3 Stimmen

Ein weiterer Punkt ist, dass dies nur verhindert, dass "new" von außerhalb der Klassenhierarchie aufgerufen wird, d.h. es ist möglich, dass ein Mitglied von "X" die Funktion aufruft. Mit der neuen C++ '0x-Funktion "=delete" können Sie ausdrücklich verhindern, dass die Funktion jemals aufgerufen wird.

6 Stimmen

Richard, nein, diese Methoden können niemals aufgerufen werden, da sie nur deklariert, aber nicht definiert sind. Der Unterschied besteht darin, dass der private Zugriff eher zu einem Linker- als zu einem Compiler-Fehler führt.

4 Stimmen

Dies verhindert nicht, dass X *x = ::new X; , die explizit den globalen Operator new aufruft, nicht den Klassenoperator new...

7voto

DrPizza Punkte 17330

Ich bin von Ihrer Motivation nicht überzeugt.

Es gibt gute Gründe, RAII-Klassen im freien Handel zu erstellen.

Ich habe zum Beispiel eine RAII-Sperrklasse. Ich habe einen Pfad durch den Code, wo die Sperre nur notwendig ist, wenn bestimmte Bedingungen halten (es ist ein Videoplayer, und ich muss nur die Sperre während meiner Rendering-Schleife halten, wenn ich ein Video geladen und spielen habe; wenn nichts geladen ist, brauche ich es nicht). Die Möglichkeit, Sperren für den freien Speicher zu erstellen (mit einem unique_ptr ) ist daher sehr nützlich; es ermöglicht mir, denselben Codepfad zu verwenden, unabhängig davon, ob ich das Schloss herausnehmen muss.

d.h. etwa so:

unique_ptr<lock> l;
if(needs_lock)
{
    l.reset(new lock(mtx));
}
render();

Wenn ich nur Sperren auf dem Stapel erstellen könnte, könnte ich das nicht tun....

0 Stimmen

Ein interessanter Punkt. Dafür gebe ich Ihnen +1. Beachten Sie jedoch, dass es einige Situationen gibt, in denen das RAII-Idiom nicht unbedingt optional ist. Wie auch immer, vielleicht ein besserer Weg, um Ihr Dilemma zu nähern ist, um einen Parameter zu Ihrer Sperre Konstruktor, der angibt, ob die Sperre benötigt wird, hinzuzufügen.

0 Stimmen

Zum Beispiel: class lock { mutex& m; bool dolock; public: lock(mutex& m_, bool dolock_) : m(m_), dolock(dolock_) { if (dolock) m.lock(); } ~lock() { if (dolock) m.unlock(); } }; Dann könnte man schreiben: lock l(mtx, needs_lock); render();

0 Stimmen

Ein Fall, in dem die Verhinderung new ist unerlässlich: wenn Ihre RAII-Klasse intern einen statischen Stapel aller lebenden Objekte dieser Klasse unterhält und davon ausgeht, dass Objekte der Klasse in umgekehrter Reihenfolge zerstört werden, in der sie erstellt wurden. Ich verwende dies, um den OpenGL-Status zu erhalten, als Ersatz für das veraltete glPushAttrib / glPopAttrib Funktionen.

2voto

Kevin Punkte 24023

@DrPizza:

Das ist ein interessanter Punkt, den Sie ansprechen. Beachten Sie jedoch, dass es einige Situationen gibt, in denen das RAII Idiom nicht unbedingt optional ist.

Wie auch immer, vielleicht ist ein besserer Weg, um Ihr Dilemma zu nähern ist, um einen Parameter zu Ihrer Sperre Konstruktor, der angibt, ob die Sperre benötigt wird hinzufügen. Zum Beispiel:

class optional_lock
{
    mutex& m;
    bool dolock;

public:
    optional_lock(mutex& m_, bool dolock_)
        : m(m_)
        , dolock(dolock_)
    {
        if (dolock) m.lock();
    }
    ~optional_lock()
    {
        if (dolock) m.unlock();
    }
};

Dann könnten Sie schreiben:

optional_lock l(mtx, needs_lock);
render();

0voto

DrPizza Punkte 17330

In meiner speziellen Situation, wenn die Sperre nicht notwendig ist, existiert die Mutex nicht einmal, so dass ich denke, dass dieser Ansatz eher schwieriger zu passen wäre.

Was ich wirklich nicht verstehe, ist die Rechtfertigung für das Verbot der Erstellung dieser Objekte im freien Speicher.

1 Stimmen

Die Begründung dafür ist, dass dies einfach eine Möglichkeit ist, eine Regel durchzusetzen, damit der nächste Entwickler nicht versehentlich vergisst, eine Sperre zu löschen (was zu einer Sperrung führen würde).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X