2016-12-11 1 views
6

Ich habe eine Objekthierarchie und muss in der Lage sein, Objekte aus der Basisklasse zu klonen. Ich habe das typische CRTP-Muster verfolgt, außer dass ich auch die Kindklasse zurückgeben möchte, wenn die Kopie direkt für ein Kind aufgerufen wird. Um das zu tun, habe ich den Vorschlag hier: https://stackoverflow.com/a/30252692/1180785CRTP-Kopiermethode warnt vor möglichem Speicherleck

Es scheint gut zu funktionieren, aber Clang warnt mich, dass ich ein potenzielles Speicherleck haben.

template <typename T> 
class CRTP { 
protected: 
    virtual CRTP<T> *internal_copy(void) const { 
     return new T(static_cast<const T&>(*this)); 
    } 

public: 
    T *copy(void) const { 
     return static_cast<T*>(internal_copy()); 
    } 

    virtual ~CRTP(void) = default; 
}; 

class Impl : public CRTP<Impl> { 
}; 

int main(void) { 
    Impl a; 
    Impl *b = a.copy(); 
    delete b; 
} 

Soweit ich das beurteilen kann, gibt es kein mögliche Speicherleck gibt, aber Clang durch XCode läuft zeigt dies: Ich habe den Code unten auf diese MCVE reduziert

Clang potential memory leak

Ist dort ein Speicherleck hier? Wenn nicht, was verursacht das falsche Positive und wie kann ich es umgehen? (Ich würde die statische Analyse lieber nicht abschalten)

+0

Das Programm, das Sie zeigen [ruft nicht 'CRTP :: copy'] (http://rextester.com/ UBB92957) überhaupt. Ich vermute, dass der Code, den Sie ausführen, von dem, den Sie anzeigen, abweichen kann. –

+0

@IgorTandetnik guter Punkt; Das habe ich vermisst, während ich es reduziert habe. Die Warnung, die ich gepostet habe, stammt jedoch direkt aus dem Code, den ich gepostet habe, also führt sie tatsächlich CRTP :: copy aus. Das lässt mich glauben, dass dies ein Fehler im Analysator sein könnte, der mit virtuellen Methoden zusammenhängt. – Dave

+0

@IgorTandetnik Ich habe den Code mit einer verbesserten Demonstration des Problems aktualisiert, die die unnötige "virtuelle" auf der Kopie entfernt und das Array entfernt. Dieser Aufruf ruft tatsächlich die CRTP :: copy-Methode auf, und die Clang-Analyse ist dieselbe. – Dave

Antwort

1

Ich habe einen Workaround gefunden, der den Analysator glücklich macht und trotzdem die Verwendung dieses Musters erlaubt. Reverse einfach die Verbindung zwischen copy und internal_copy:

template <typename T> 
class CRTP : public Base { 
protected: 
    virtual CRTP<T> *internal_copy(void) const { 
     return copy(); 
    } 

public: 
    T *copy(void) const { 
     return new T(static_cast<const T&>(*this)); 
    } 
}; 

Dies funktioniert nach wie vor im Rahmen des ursprünglichen Vorschlags here, denn wenn copy innen CRTP Lösung, wird es CRTP die Überschreibung (auch vorziehen, wenn es nicht eine virtuelle Methode ist), also gibt es keine Endlosschleife.

Warum der Analysator mit dieser Reihenfolge zufrieden ist, aber mit der ursprünglichen Reihenfolge nicht zufrieden ist, habe ich keine Ahnung.

1

Ich denke, der Analysator wird durch die static_cast in Ihrer copy Methode verwirrt. Wenn Sie es einfach in dynamic_cast ändern, hört es auf, die Zeile als ein Speicherleck zu kennzeichnen. Dies führt zu der Annahme, dass Sie meinen, dass Sie möglicherweise eine Basistypinstanz (CRTP<T>) in den abgeleiteten Typ (T) umwandeln, die bei einer Dereferenzierung natürlich ungültig wäre. Du machst das offensichtlich nicht, also könnte das ein Fehler im Leckdetektor sein, oder es könnte etwas subtiler sein als das, worüber ich gerade nachdenke. Es ist jetzt möglicherweise kein Fehler, aber wenn Impl ein komplizierterer Typ wird (z. B. einer mit mehrfacher Vererbung), kann dies ein Problem werden (ebenso wie Ihr static_cast beim Aufruf von CTOR-Kopie).

Diese Implementierung (mit weniger Abgüsse) hatte null Lecks auch für mich:

template <typename T> 
class CRTP { 
protected: 
    virtual T *internal_copy() const { 
     return new T(static_cast<const T&>(*this)); 
    } 

public: 
    T *copy() const { 
     return internal_copy(); 
    } 

    virtual ~CRTP() = default; 
}; 
+0

In meiner Antwort auf Ihren Kommentar finden Sie eine Erklärung, warum 'internal_copy' in diesem Anwendungsfall' T * 'nicht zurückgeben kann. Interessant, dass 'dynamic_cast' es glücklich macht; Es ist nicht etwas, das ich getestet habe, weil ich '-no-rtti' benutze, aber wie du sagst; vielleicht denkt es nur, dass static_cast in den falschen Typ umgewandelt werden könnte. – Dave

Verwandte Themen