2013-12-19 17 views
6

Ich habe einige Legacy-Ära Code bei der Arbeit, die in einem Doppel-Zeiger und vergibt Speicher zu ihm. Ein verkürztes Beispiel dafür wäre etwa so aussehen:Verwenden von freigegebenen Zeigern mit Speicher in einer anderen Funktion

struct LegacyObj 
{ 
    int a; 
    double b; 
}; 

void LegacyAllocator(LegacyObj** ppObj) 
{ 
    *ppObj = (LegacyObj*)malloc(sizeof(LegacyObj)); 
} 

void LegacyDeleter(LegacyObj** ppObj) 
{ 
    free(*ppObj); 
} 

Die tatsächliche LegacyAllocator Funktion ist ~ 100 Zeilen und Mischungen Lesen aus Dateien mit einer verketteten Liste von LegacyObj Zeiger zu schaffen, und ist nicht etwas, was ich in der Lage sein würde um mit dem Umschreiben gerade fertig zu werden. Ich möchte jedoch die Verwendung dieser Funktion ein wenig sicherer machen und Speicherlecks vermeiden, die von Ausnahmen & TC auftreten können. Die erste Lösung, die ich mir ausgedacht hatte, war, sie in eine Klasse zu packen und die alten Funktionen im ctor/dtor aufzurufen.

class RAIIWrapper 
{ 
public: 
    RAIIWrapper() 
     :obj{nullptr} 
    { 
     ::LegacyAllocator(&obj); 
    } 
    RAIIWrapper(RAIIWrapper&& that) 
     : obj{ that.obj} 
    { 
     that.obj = nullptr; 
    } 
    RAIIWrapper& operator=(RAIIWrapper&& that) 
    { 
     RAIIWrapper copy{std::move(that)}; 
     std::swap(obj, copy.obj); 
     return *this; 
    } 
    ~RAIIWrapper() 
    { 
     ::LegacyDeleter(&obj); 
    } 

private: 
    LegacyObj* obj; 
}; 

Aber ich bin neugierig - ist es eine Möglichkeit, diese std::shared_ptr oder std::unique_ptr mit zu tun? Ich war nicht in der Lage, eine Lösung zu finden, ohne den Originalzeiger an LegacyAllocator übergeben zu müssen.

+0

'unique_ptr' und' shared_ptr' nehmen nicht Verteilern, sie nur nehmen Löscher. Sie würden den Zeiger selbst mit 'LegacyAllocator (& obj)' belegen und einen Deletier-Funktor für das 'unique_ptr' /' shared_ptr' erstellen, der 'LegacyDeleter (& p)' in seinem 'operator()' – David

Antwort

3

Ja, können Sie eine benutzerdefinierte deleter mit std::unique_ptr oder std::shared_ptr verwenden, zum Beispiel:

struct Deleter { 
    void operator()(LegacyObj *p) const { 
    LegacyDeleter(&p); 
    } 
}; 

std::unique_ptr<LegacyObj, Deleter> MakeLegacyObj() { 
    LegacyObj *p = 0; 
    LegacyAllocator(&p); 
    return std::unique_ptr<LegacyObj, Deleter>(p); 
} 

std::unique_ptr<LegacyObj, Deleter> p = MakeLegacyObj(); 

Und wie darauf richtig durch @ Dave aus, funktioniert dies mit shared_ptr zu:

std::shared_ptr<LegacyObj> p = MakeLegacyObj(); 
+1

und 'std :: shared_ptr aufruft p = MakeLegacyObj(); 'funktioniert auch – David

+0

@Dave: Ja, guter Punkt. – vitaut

+0

Danke, sieht aus wie ein benutzerdefiniertes Deletor Funktor zu erstellen, was ich vermisse. –

0

Sie können unique_ptr verwenden um den Speicher zu löschen, aber Sie werden eine benutzerdefinierte Deleter Klasse liefern müssen, da der Speicher malloc anstatt new Verwendung zugewiesen wird.

Besser noch, ändern Sie den Zuweisungscode, um stattdessen new zu verwenden, und verwenden Sie einfach unique_ptr. Wenn Sie diese Straße hinuntergehen, können Sie einfach den Zuordner unique_ptr anstelle eines Zeigers auf den Speicher zurückgeben lassen.

Angenommen, Sie benötigen, um Ihre eigenen benutzerdefinierten deleter zu schaffen, ist hier ein, wie Sie es tun könnte:

template <typename T> 
class MallocDeleter 
{ 
public: 
    void operator() (T* obj) const 
    { 
    LegacyDeleter (*obj); 
    } 
}; 

typedef std::unique_ptr <LegacyObj, MallocDeleter <LegacyObj>> unique_legacy_ptr; 

Sie könnten auch wahrscheinlich bieten eine make_unique_legacy Art Funktion, die LegacyAllocator durch den Aufruf zuordnet, anstatt zu initialisieren die unique_ptr selbst.

0

Sie können eine Factory-Funktion für unique_ptr s wie folgt erstellen:

typedef void(* LegacyDeleterType)(LegacyObj*); 
typedef std::unique_ptr<LegacyObj,LegacyDeleterType> UniqueLegacyPtr; 

UniqueLegacyPtr makeUniqueLegacyObj() 
{ 
    LegacyObj * p = nullptr; 
    LegacyAllocator(&p); 
    return UniqueLegacyPtr(p, [](LegacyObj*p){ LegacyDeleter(&p); }); 
} 

Sie können nun, dass verwenden unique_ptr s zu erstellen, und Sie können auch auf shared_ptr s die Erfassung der Brauch deleter automatisch auf dem Bau zuordnen:

int main() 
{ 
    auto unique = makeUniqueLegacyObj(); 
    std::shared_ptr<LegacyObj> shared = makeUniqueLegacyObj(); 
} 
Verwandte Themen