2011-01-10 9 views
0

Meine GUI-Anwendung unterstützt polymorphe zeitgesteuerte Ereignisse, so dass der Benutzer neue und die GUI-Aufrufe löscht. Dies kann zu einem Problem führen, wenn die Laufzeiten nicht kompatibel sind.Wird dies ein Problem mit verschiedenen Laufzeiten mit DLL verursachen?

So wurde mir gesagt, eine vorgeschlagene Lösung sein würde:

class base; 

class Deallocator { 
    void operator()(base* ptr) 
    { 
     delete ptr; 
    } 
} 

class base { 
public: 
base(Deallocator dealloc) 
{ 
    m_deleteFunc = dealloc; 
} 
~base() 
{ 
    m_deleteFunc(this); 
} 

private: 
Deallocator m_deleteFunc; 
} 

int main 
{ 
    Deallocator deletefunc; 

    base baseObj(deletefunc); 
} 

Während dies eine gute Lösung ist, tut es verlangen, dass der Benutzer ein Deallocator-Objekt erstellen, das will ich nicht. Ich frage mich aber, ob ich eine Deallocator zu jeder abgeleiteten Klasse bereitgestellt: zB

class derived : public base 
{ 
    Deallocator dealloc; 
public: 
    Derived() : base(dealloc); 
{ 
} 
}; 

Ich denke, das immer noch nicht, obwohl nicht funktioniert. Die Einschränkung ist: Die Funktion addTimedEvent() ist Teil der Widget-Klasse, die ebenfalls in der DLL enthalten ist, aber vom Benutzer instanziiert wird. Die andere Einschränkung besteht darin, dass einige Klassen, die von Widget abgeleitet sind, diese Funktion mit ihren eigenen zeitgesteuerten Ereignisklassen aufrufen.

Vorausgesetzt, dass "er, der neu angerufen hat, löschen muss" was könnte angesichts dieser Einschränkungen funktionieren?

Dank

+0

Sie müssen die Basisklasse destructor virtuell machen. –

+0

@Hans: Für den vorgeschlagenen (gebrochenen) Mechanismus in der Frage, ja sollte der Destruktor virtuell sein. Aber viele der bekannten Wege, das Problem zu lösen, erfordern keine virtuelle Zerstörung. –

+0

Dieser [Blogpost] (http://blogs.msdn.com/b/oldnewthing/archive/2006/09/15/755966.aspx) bietet einige Lösungen für dieses Problem. – Naveen

Antwort

1

Ich schlage vor, dass Sie die COM Referenzzählung Paradigma (AddRef und Release) zu studieren. Dies ermöglicht eine flexiblere Lebensdauer und garantiert, dass der richtige Deallocator verwendet wird, da das Objekt sich selbst löscht.

Bitte beachten Sie, dass wenn Sie Klassenobjekte über DLL-Grenzen hinweg teilen, könnten Sie viel größere Probleme haben, die nur den gleichen Zuordner verwenden. Es gibt die gesamte Ein-Definition-Regel, die Konventionen, Datenlayout und Namensveränderungsschemata, die sich zwischen Compilern unterscheiden, berücksichtigt und aufruft. Wenn Sie also eine wiederverwendbare Bibliothek haben wollen, müssen Sie wirklich die COM-Methode anwenden, die mit Referenzzählung, Selbstlöschung und einer Schnittstelle arbeitet, die nur reine virtuelle Funktionen enthält. Ob Sie echte COM-Objekte oder Ihr eigenes COM-ähnliches System erstellen, hängt von Ihren anderen Anforderungen ab.

0

Das erste, was einem einfällt, ist, der Basisklasse eine virtuelle (abstrakte?) SelfDestruct Methode zu geben. Angenommen, der Benutzer Ihrer DLL übergibt eine Klasse, die er selbst abgeleitet hat, wird er wissen, wie er die Zuordnung rückgängig machen kann.

Wenn er Klassen, die Sie geschrieben haben, bestehen kann, dann haben Sie mehr Probleme. Ich schlage vor, die Zuweisung solcher Klassen zu verbieten und eine statische Methode für die Zuweisung dieser Klassen mit Ihrem eigenen Zuordner bereitzustellen.

Ich bin mir nicht sicher, ob ich meine Idee sehr klar erklärt habe ... wenn nicht, bitte fragen Sie, ich werde später Code zur Verfügung stellen.

0

Was mit den gegebenen Einschränkungen funktionieren könnte, ist, dass Sie jedem TimedEvent einen Deleter-Funktionszeiger zuordnen, wobei beide als Argumente für addTimedEvent angegeben sind.
Um die Last des Clients zum Erstellen einer benutzerdefinierten Löschfunktion zu entlasten, können Sie eine Inline-Löschfunktion als Mitglied des anonymen Namespace in der Kopfzeile Ihrer Widgetklasse bereitstellen.

Zum Beispiel:

// Widget header 

class base; 
namespace { 
    inline void default_deleter(base* p) 
    { 
    delete p; 
    } 
} 

class Widget 
{ 
public: 
    addTimedEvent(base* event, void(*deleter)(base*)); 
}; 

Der Vorteil der Inline-Funktion ist, dass es im Zusammenhang mit dem Client-Code kompiliert werden, so delete wird auch ein kompatibles deallocator als Client verwenden, verwendet, um die Veranstaltung reservieren .

Bearbeiten: Die Löschfunktion wurde ein Mitglied des anonymen Namespace. Dies ist erforderlich, um ODR-Verletzungen zu vermeiden.
Ohne den Namespace erhalten Sie zwei Funktionen , die denselben externen Namen haben (also sind sie die gleichen wie der Linker betroffen), aber mit unterschiedlicher Semantik, weil sie sich auf unterschiedliche Deallocators beziehen.
Mit dem anonymen Namespace werden alle Instanzen von default_deleter separate Entitäten für den Linker. Dies hat den (unglücklichen) Nebeneffekt, dass Sie die Funktion nicht mehr als Standardargument für addTimedEvent verwenden können.

+0

Funktioniert das noch wie erwartet, wenn das Widget neu heißt? Wenn nicht, wie könnte das geschehen? Ist es auch eine Thread-sichere Lösung? Danke – jmasterx

+0

Nein, es ist nicht sicher. Inline-Funktionsdefinitionen in Typen, die über Modulgrenzen verteilt sind, verursachen alle Arten von ODR-Verletzungen –

+0

@Ben Voigt: Sie haben Recht. Ich habe die Antwort aktualisiert, um die ODR-Verstöße zu vermeiden. –

Verwandte Themen