2017-07-04 3 views
0

Kürzlich stieß ich auf ein Beispiel, dass mein Destruktor einen Parameter nehmen muss.Warum Destruktoren Parameter in C++ nicht akzeptiert?

Ich arbeite an einem C-Paket, das Speicher intern verwaltet und verfolgt die Zuweisung und Freigabe mit eigenen Parametern. Ich will das nicht brechen.

Ich schrieb einen C-Code, der meine eigenen Datenstrukturen initialisiert und am Ende freigibt. Als ich mich entschloss, nach C++ zu migrieren, wurde mir klar, dass das Zuordnen und Freigeben von Datenstrukturen in Konstruktoren und Destruktoren platziert werden sollte. Also habe ich diese Funktionen in Konstruktor und Destruktor geändert. Mein Problem ist jetzt, dass ich einen Parameter verwenden muss, um an den Destruktor zu übergeben, um zugewiesene Daten freizugeben; Dieser Parameter ist nicht in meinem eigenen Code und ist C und ich möchte mich damit nicht anlegen.

Meine Frage ist zweifach:

Warum C++ in Destruktoren in erster Linie nicht zu akzeptieren Parameter entschieden? und Was sind meine Optionen? (Ich kann einen Zeiger auf diese Parameter speichern oder irgendwie diese Funktion in meinem destructor nennen, aber es scheint nicht eine gute Praxis der C++ Programmierung)

Update: einige Code hinzufügen Hier ist meine imaginäre Klasse:

class paru_symbolic/* paru_symbolic*/ 
{ 
    public: 
     paru_symbolic (cholmod_sparse *A, cholmod_common *cc); // constructor 
     ~paru_symbolic (cholmod_common *cc); // destructor 

     // ------------------------------------------------------------------------- 
     // row-form of the input matrix and its permutations 
     // ----------------------------------------------------------------------- 
       //--- other stuff 
        ... 
}; 

Hier ist mein aktueller C-Konstruktor:

#include "Parallel_LU.hpp" 
paru_symbolic *paru_sym_analyse 
(
// inputs, not modified 
    cholmod_sparse *A, 
// workspace and parameters 
    cholmod_common *cc){ 

    DEBUGLEVEL(0); 
... 
    aParent = (Int*) paru_alloc (m+nf, sizeof(Int),cc); 

... 
} 

und destructor:

void paru_freesym (paru_symbolic **LUsym_handle, 
      // workspace and parameters 
    cholmod_common *cc 
){ 
    DEBUGLEVEL (0); 
    if (LUsym_handle == NULL || *LUsym_handle == NULL){ 
     // nothing to do; caller probably ran out of memory 
     return; 
    } 

    paru_symbolic *LUsym; 
    LUsym = *LUsym_handle; 

    Int m, n, anz, nf, rjsize; 
... 
    cholmod_l_free (m+nf, sizeof (Int), LUsym->aParent, cc); 

... 
    cholmod_l_free (1, sizeof (paru_symbolic), LUsym, cc); 

    *LUsym_handle = NULL; 
} 

Der Parameter cc wird innerhalb des SuiteSparse-Pakets zum Verfolgen von Zuordnungen und Freigeben von Daten verwendet. Es wurde bereits im SuiteSparse-Paket verwendet und ist ein nützliches Tool zum Verfolgen von Speicher. Einige Leute erwähnt, dass, wer einen Parameter an den Destruktor übergeben will. Das ist ein guter Punkt, aber wir könnten die gleichen Parameter haben, die wir im Konstruktor als Standard haben.

+1

Mögliches Duplikat von [Destruktor-Parameter] (https://StackOverflow.com/questions/6245295/destructor-parameters) –

+0

Klassen sollen in sich abgeschlossen sein, und Schnittstellen sollen relativ eng sein. Wenn eine Klasse Daten enthält, die verwaltet werden müssen (zum Beispiel, die zum Zeitpunkt des Zerstörens freigegeben werden müssen), sollte die Klasse alles darüber wissen. Es ist schwer vorstellbar, dass Daten an den Destruktor übergeben werden müssen, die die Klasse bereits intern (also in Member-Variablen) nicht kennen sollte. Was ist Ihr Beispiel für Parameter, die Sie an den Destruktor übergeben müssen? –

+0

Wie würden Sie die Parameter übergeben? – drescherjm

Antwort

2

@ Vittorios Antwort hat den allgemeinen Ansatz. Ich werde es einfach an Ihren hinzugefügten Beispielcode anpassen.

Die Art und Weise Sie das Problem präsentiert sich wie folgt:

  1. Sie verwenden ein Dienstprogramm Typ cholmod_common einige nützliche Arbeit für Ihre Art zu tun. Die Instanz dieses Typs kann an mehrere paru_symbolic Objekte übergeben werden, die Sie erstellen.
  2. Keine einzige Instanz von paru_symbolic besitzt es einzigartig, aber sie alle hängen am Anfang und am Ende ihrer Lebensdauer davon ab. Es muss für die gleiche Dauer wie alle diese Objekte existieren.

Das schreit std::shared_ptr zu mir.

class paru_symbolic 
{ 
    std::shared_ptr<cholmod_common> _cc; 
    public: 
     paru_symbolic (cholmod_sparse *A, std::shared_ptr<cholmod_common> cc) 
     : _cc(cc) { 
      // constructor 
     } 
     ~paru_symbolic() { 
      // use _cc , it's guaranteed to live until the closing brace at the least. 
     } 
}; 

Das ist es. Das Eigentum ist jetzt geteilt. Das letzte Objekt, das von der Instanz cholmod_common abhängig ist, ist derjenige, der es ohne zusätzliche Arbeit von Ihnen säubert.

+0

Ich denke, das ist die Antwort, die ich brauche. Cc wird jedoch nicht in meinem Konstruktor initialisiert. Ich habe es von woanders ausgeliehen und übergebe es an den Konstrukteur. Ich kann es anders gestalten, obwohl – Aznaveh

+1

@Aznaveh - Wenn Sie es neu gestalten können, das ist gut. Der Hauptpunkt ist, dass es niemals einen rohen "cholmod_common" -Zeiger geben sollte. Stattdessen sollte es sofort in einem 'std :: shared_ptr 'erstellt werden, bevor * das * shared_ptr an die Konstruktoren abhängiger Klassen übergeben wird, um den gemeinsamen Besitz zu begründen. – StoryTeller

14

Parameter in Destruktoren sind nicht sinnvoll, da Destruktoren automatisch aufgerufen werden, wenn ein Objekt den Gültigkeitsbereich verlässt. Wie können Sie einen Parameter an diesen übergeben?

{ 
    Foo x; 
} // `~Foo()` is automatically called here 

Was Sie wahrscheinlich wollen, ist die Ressource in Ihrer Klasse zu speichern. Hier ist ein unrealistisches Beispiel mit einer dynamisch zugewiesenen Zeiger:

struct RAIIPointer 
{ 
    Foo* ptr; 

    // Initialize the resource 
    RAIIPointer() : ptr{new Foo{/*...*/}} 
    { 
    } 

    RAIIPointer(const RAIIPointer&) = delete; 
    RAIIPointer& operator=(const RAIIPointer&) = delete; 

    RAIIPointer(RAIIPointer&&) { /* implement appropriately */ } 
    RAIIPointer& operator=(RAIIPointer&&) { /* implement appropriately */ } 

    // Release the resource 
    ~RAIIPointer() 
    { 
     delete ptr; 
    } 
}; 

Beachten Sie, dass in einem realen Szenario würden Sie std::unique_ptr verwenden, und wenn Sie wurden Ihre eigene RAII Ressourcenklasse Implementierung würden Sie die richtige Kopieren/Verschieben implementieren müssen Operationen.

+0

Warum der Downvote? –

+0

Kontern, nicht sicher, was hier falsch war. – Borgleader

+0

Ich verstehe, dass dies eine ungenaue Antwort * ist (z. B. können Sie Destruktoren manuell aufrufen, wenn Sie wollen) *, aber ich habe versucht, es auf die Erfahrungsebene des OPs anzupassen. –

0

In C++ wird diese Art von Vorgang über eine allocator durchgeführt, siehe auch std::allocator. So könnten Sie Ihren eigenen Zuordner mit einem Datenelement cholmod_common* erstellen.

Alternativ können Sie den Parameter deleter für die Speicherverwaltung über Smartpointer verwenden. Zum Beispiel

class foo 
{ 
    struct bar_deleter { 
    cholmod_common *cc; 
    bar_deleter(cholmod_common *c) : cc(c) {} 
    void destroy(bar*) { cc->free(bar); } 
    }; 
    std::unique_ptr<bar,bar_deleter> data; 
public: 
    foo(some_type const&input, cholmod_common *cc) 
    : data(cc->create<bar>(input), bar_deleter(cc)) {} 
}; 

Was natürlich, hält auch ein cholmod_common* mit den Daten.

Der Name „cholmod_common“ deutet darauf hin, dass dies eine gemeinsame Ressource ist, das heißt nur eine cholmod_common Objekt zu einem bestimmten Zeitpunkt ist. Iff Dies ist so, dann könnten Sie einen Stateless-Allocator verwenden, der die gemeinsame Ressource als statisches Datenelement enthält (oder über eine singleton erhält).

Verwandte Themen