2010-05-17 8 views
13

Ich verstehe nicht den Zweck von boost :: checked_delete. Die Dokumentation sagt:Zweck der boost :: checked_delete

der C++ Standard ermöglicht es, in 5.3.5/5, Zeiger auf unvollständige Klassentypen zu mit einem Löschausdruck gelöscht werden. Wenn die Klasse eine nicht-triviale destructor hat, oder eine klassenspezifische Operator löschen, ist das Verhalten undefiniert. Einige Compiler Ausgabe eine Warnung, wenn ein unvollständiger Typ ist gelöscht, aber leider nicht alle tun, und Programmierer manchmal ignorieren oder Warnungen deaktivieren.

Die mitgelieferte Funktion und Klasse Vorlagen können diese Probleme zu verhindern, verwendet werden, da sie eine vollständige Art erfordern und verursachen einen Kompilierungsfehler anders.

So der C++ Standard ermöglicht es Ihnen, unvollständige Typen zu löschen, die sie, wenn der Typ einen nicht-trivialen destructor hat nicht definiertes Verhalten verursacht. Was? Wie kann ein unvollständiger Typ einen Destruktor überhaupt haben? Ist ein unvollständiger Typ nicht nur ein Prototyp?

+0

Könnte das etwas mit dem Schneiden zu tun haben? http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c – Cogwheel

Antwort

16

Das häufigste Beispiel für einen unvollständigen Typ ist eines, das erklärt nur wurde:

class foo 
{ 
public: 
    ~foo() { ... }; 
}; 

Aber wenn oben:

// this file does not include the definition of foo 

class foo; 

void bad(foo *f) 
{ 
    delete f; // undefined behavior if there exists foo::~foo 
} 

In Wirklichkeit wie dies die Definition von foo aussehen Code hat die Klassendefinition nicht 'gesehen' und sieht nur die Klassendeklaration, der Code wird kompiliert.

+0

Wie ist es möglich * einen vollständigen Typ in einer Klassenvorlage * zu erfordern? –

+3

'checked_delete' versucht,' sizeof' für den Typ aufzurufen, und anscheinend verursacht dies einen Compilerfehler, wenn der Typ unvollständig ist. – Channel72

+0

@ Channel72: Sie wissen, warum C++ dies nicht als Compilerfehler definiert? –

2

C++ können Sie delete auf Variablen verwenden, die Zeiger auf unvollständigen Typen zu der Zeit sind.

struct S; // incomplete 

int main() { 
    S* s = NULL; 
    delete s; // legal 
} 

Der Compiler weiß nicht, an diesem Punkt, was S wirklich ist. Wenn es S hat einen nicht-trivialen Destruktor, dann ist der Compiler nicht erforderlich, um dieses Problem zu erkennen.

Praktisch, was wahrscheinlich passiert ist, dass, wenn der Compiler auf die delete Anweisung auf einen unvollständigen Typ trifft, füllt es einen Aufruf an, was erwartet wird, wird der gewöhnliche Compiler des Typs Standarddestruktor generieren. Und wenn das der Destruktor ist, dann ist alles in Ordnung. Aber wenn es sich herausstellt, dass S einen nicht-trivialen Destruktor hat, oder wenn es eine eigene spezielle Methode zum Löschen anbietet, dann ist der Inhalt, den der Compiler früher ausgefüllt hat, falsch. Der Compiler durfte jedoch davon ausgehen, dass er die delete-Anweisung korrekt kompiliert hat und niemals zurückblickt. Wenn diese Annahme falsch ist, erhalten Sie undefiniertes Verhalten.

Die Boost-Funktion stellt sicher, dass sie nur bei vollständigen Typen aufgerufen wird, wodurch das undefinierte Verhalten vermieden wird, das bei unvollständigen Typen auftreten kann.

4

Beachten Sie Folgendes:

foo.h:

#ifndef Foo_H 
#define Foo_H 
#include <boost/scoped_ptr.hpp> 
#include <boost/utility.hpp> 

class Foo : private boost::noncopyable 
{ 
public: 
    Foo(); 
    ~Foo(); 

    void do_something_interesting(); 

private: 
    class Impl; // incomplete type 
    boost::scoped_ptr<Impl> impl; 
}; 

#endif 

Foo.cav:

#include "Foo.h" 
#include <string> 
#include <iostream> 

class Foo::Impl 
{ 
public: 
    Impl() : name("Foo::Impl") 
    {} 

    void say_hi() 
    { std::cout << name << " says hi!" << std::endl; } 

    std::string name; 
}; 

Foo::Foo() 
: impl(new Impl) 
{} 

Foo::~Foo() 
{} 

void Foo::do_something_interesting() 
{ impl->say_hi(); } 

diese (erfundene) Gegeben, können Sie auch nicht Foo::Foo oder Foo::~Foo inline, da der Typ nicht vollständig ist. Wenn Sie beide in einem Kontext definieren, in dem der Typ Foo::Impl ein vollständiger Typ ist, können Sie den Typ sicher löschen. boost::checked_delete macht diese Sicherheitsprüfung für Sie, und es ist nur eine Kompilierzeit Kosten. Wenn Sie entweder Foo::~Foo inline oder es vollständig weglassen, erhalten Sie einen Fehler von boost::checked_delete, wo immer Sie versuchen, eine Foo Instanz zu zerstören.