2016-10-19 3 views
6

Ich arbeite in einem Projekt mit einer großen Legacy-Code-Basis und habe versucht, Teile davon neu zu gestalten, um weg von altem C-Style-Code zu kommen.Warum werden meine Ergebnisdaten als void * zurückgegeben?

Ich bin auf ein Problem gestoßen und habe ein kurzes Programm zur Erklärung vorbereitet.

Die alte Schnittstelle, die ich benutze, benötigt einen Zeiger auf die Ergebnisdaten als void * und ich möchte vermeiden, dies ändern zu müssen.

Die unique_ptr im Beispiel zeigt nur, dass in meiner realen Codebasis alles, was an den Daten arbeitet, intelligente Zeiger zur Speicherverwaltung verwendet.

Mein Problem ist, dass die Ergebnisdaten kaputt gehen (siehe letzte Ausgabezeile/letzter Aufruf von printPayload); am Ende ist alles 0, aber es scheint kein Problem bei der Umwandlung in den void * und zurück zu sein, wie durch die 2. und 3. Ausgabezeile gezeigt.

Ist dies ein Problem im Zusammenhang mit Provisorien? Ich verstehe es nicht ...

Ich hoffe, diese Art von Problem hat Relevanz für einige von Ihnen.

#include <iostream> 
#include <memory> 

struct Payload 
{ 
    long a; 
    int b; 
    int c; 

    Payload() : a(), b(), c() {} 
    Payload(long setA, int setB, int setC) : a(setA), b(setB), c(setC) {} 
}; 

void printPayload(const Payload& printThis) 
{ 
    std::cout << "payload -- a: " << printThis.a << " b: " << printThis.b << " c: " << printThis.c << std::endl; 
} 

void doSomething(Payload* sourceData, void* targetData) 
{ 
    if (!sourceData) return; 

    std::unique_ptr<Payload> sourceDataUnique(sourceData); 

    sourceDataUnique->a = 222; 
    sourceDataUnique->b = 333; 
    sourceDataUnique->c = 444; 

    printPayload(*sourceDataUnique); 

    targetData = reinterpret_cast<void*>(sourceDataUnique.release()); 

    printPayload(*(reinterpret_cast<Payload*>(targetData))); 
} 

int main(void) 
{ 
    Payload* myPayload = new Payload(14, 8, 1982); 
    Payload myResult; 

    printPayload(*myPayload); 

    doSomething(myPayload, &myResult); 

    printPayload(myResult); 
} 

Ausgang:

payload -- a: 14 b: 8 c: 1982 
payload -- a: 222 b: 333 c: 444 
payload -- a: 222 b: 333 c: 444 
payload -- a: 0 b: 0 c: 0 
+0

Sie setzen 'targetData' in 'doSomething', aber diese Änderung ist lokal für die Funktion. Der Parameter sollte eine Referenz sein. –

+0

Dies kann vereinfacht werden: 'void doSomething (Payload * src, Payload * dst) {dst = src; } ' – melpomene

+0

@ PankajDaga, das wird nicht funktionieren, da es eine Referenz auf eine temporäre sein wird. – StoryTeller

Antwort

8

targetData ist ein lokale Variable doSomething. Nachdem Sie ihm eine Adresse zugewiesen haben, wird er nicht mehr angezeigt.

Sie eigentlich nie zuweisen myResult.

3

In Ihrem Code ist der Parameter targetData lokal zu doSomething Funktion (d. H. Jede Änderung geht verloren, nachdem Sie den Gültigkeitsbereich der Funktion verlassen haben). *targetData bezieht sich jedoch auf die Variable myResult, die in main Funktion deklariert ist.

der folgende Code sollte also funktionieren:

void doSomething(Payload* sourceData, void* targetData) 
{ 
    if (!sourceData) return; 

    sourceData->a = 222; 
    sourceData->b = 333; 
    sourceData->c = 444; 

    printPayload(*sourceData); 

    Payload* td = static_cast<Payload*>(targetData); 
    *td = *sourceData; 
    printPayload(*td); 
} 
+0

@melpomene natürlich. Es war ein Tippfehler. Ich werde die Quelle bearbeiten. Vielen Dank. –

+0

@ JonathanWakely ja, ich wollte nur die Funktion so ähnlich wie möglich zum ursprünglichen Code halten. Ich werde die Quelle bearbeiten. –

1

Sie kopieren nicht immer die Quelldaten an das Ziel, sondern nur die ändern die targetData Zeiger auf das Objekt.

So etwas wie dies funktionieren würde:

Payload* targetPayload = reinterpret_cast<Payload*>(targetData); 
*targetPayload = *sourceData; 

Unter Eigentum an der Quelle Nutzlast über einen Smart-Pointer ist wahrscheinlich eine schlechte Idee - wenn der anrufende Code geschrieben ist Ausnahme richtig zu handhaben, dann wird es das Objekt löscht Bei einem Fehler wird der intelligente Zeiger bedeuten, dass er zweimal gelöscht wird. Wenn der aufrufende Code nicht geschrieben wurde, um Ausnahmen korrekt zu behandeln, dann ist es Ihre Aufgabe, Code zu schreiben, der keine Ausnahme auslösen kann, mit der der intelligente Zeiger nicht hilft.

(wie es ein gegossenes zwischen Zeiger ist, Sie static_cast verwenden könnte, aber ich ziehe es potenziell gefährlich geschieht etwas etwas und reinterpret_cast erzählt andere Entwickler als void * sein. Reinterpret_cast könnte)

Verwandte Themen